Thread safety: NSOperationQueue + [array addObject] - iphone

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...
}
}
}

Related

Using an application-lifetime-thread other than the main thread

I've a multi-threading application in which each thread has to do some job, but at a certain point some code needs to be executed serially (like writing into sqlite3 database), so I'm calling that code to be performed on main thread using:
[self performSelectorOnMainThread:#selector(serialJob:) withObject:object waitUntilDone:YES];
and every thing went just fine except that when that code needs some time the user interaction with the application gets disabled until that code has been finished, so is there any way to make another ONE thread that can be run on background and can be called whenever I need it just like the main one so I can replace the previous call with:
[self performSelector:#selector(serialJob:) onThread:REQUIRED_THREAD withObject:object waitUntilDone:YES];
this thread should be some class's static data member to be accessed from all over the code.
any help would be very appreciated, and many thanks in advance...
This is quite easy to do, just spawn your thread and let it run it's runloop using [[NSRunLoop currentRunLoop] run]. That's all that is required to be able to use performSelector:onThread: with a custom thread.
If you are on iOS 4 or newer you should consider using Grand Central Dispatch queues instead of threads though. The GCD APIs are much easier to use and can utilize the system resources much better.
Like Sven mentioned, look into Grand Central Dispatch.
You can create a queue like this:
dispatch_queue_t myQueue = dispatch_queue_create("com.yourcompany.myDataQueue", NULL);
Now you can call blocks on that queue:
dispatch_async(myQueue, ^{
// Your code to write to DB.
});
When you're done, don't forget to release the queue:
dispatch_release(myQueue);
Due to the my question that I need the current thread to be blocked until the database job has been finished, I've tried these two solutions and they worked perfectly. You can either use critical sections or NSOperationQueue and I prefer the first one, here is the code for both of them:
define some class "DatabaseController" and add this code to its implementation:
static NSString * DatabaseLock = nil;
+ (void)initialize {
[super initialize];
DatabaseLock = [[NSString alloc] initWithString:#"Database-Lock"];
}
+ (NSString *)databaseLock {
return DatabaseLock;
}
- (void)writeToDatabase1 {
#synchronized ([DatabaseController databaseLock]) {
// Code that writes to an sqlite3 database goes here...
}
}
- (void)writeToDatabase2 {
#synchronized ([DatabaseController databaseLock]) {
// Code that writes to an sqlite3 database goes here...
}
}
OR to use the NSOperationQueue you can use:
static NSOperationQueue * DatabaseQueue = nil;
+ (void)initialize {
[super initialize];
DatabaseQueue = [[NSOperationQueue alloc] init];
[DatabaseQueue setMaxConcurrentOperationCount:1];
}
+ (NSOperationQueue *)databaseQueue {
return DatabaseQueue;
}
- (void)writeToDatabase {
NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(FUNCTION_THAT_WRITES_TO_DATABASE) object:nil];
[operation setQueuePriority:NSOperationQueuePriorityHigh];
[[DatabaseController databaseQueue] addOperations:[NSArray arrayWithObject:operation] waitUntilFinished:YES];
[operation release];
}
these two solutions block the current thread until the writing to database is finished which you may consider in most of the cases.

Use an NSRecursive Lock to wait for an operation to finish

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];

how to stop performing selector in background?

I have some class A. In this class i have a method,
which calls [self performSelectorInBackground:...]. And it starts downloading
some info from internet.
After i tap Home button, then enter the app again, this background method keeps working.
So, if i call this method again, i have bad_access, because background method is already working and i call it twice.
Can i stop performing selector in background of the class A? For example in my applicationDidEnterBackground?
Or can i check, if selector is performing or something?
I found couple things like
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget:a];
[NSObject cancelPreviousPerformRequestsWithTarget:a selector:#selector(startDownload) object:nil];
But they didn't work for me.
So
my objAppDelegate:
#inteface ObjAppDelegate
{
A *a;
}
#implementation ObjAppDelegate
{
-(void)applicationDidEnterBackground:(UIApplication *)application
{
//or it can be didBecomeActive..
//here. check if background task of class A is running, or just stop it ??
}
}
#implementation A
{
//some timer, or event, etc.
-(void)startDownload
{
[self performSelectorInBackground:#selector(runBackgroundTask) withObject:nil];
}
-(void)runBackgroundTask
{
//some network stuff..
}
}
i did it like this:
threadForDownload = [[NSThread alloc] initWithTarget:self selector:#selector(threadMain:) object:nil];
[threadForDownload start];
[self performSelector:#selector(startDownload) onThread:threadForDownload withObject:nil waitUntilDone:NO];
(void)threadMain:(id)data {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
while (YES) {
[runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
[pool release];
}
In my startDownload method i look at activity indicator to check, whether
startDownload is already running..
-(void)startDownload
{
if (![[UIApplication sharedApplication] isNetworkActivityIndicatorVisible]) // flag..
{
//....
}
}
// I make visible networkActivityIndicator every time i start downloading
You can easily create a BOOL instance variable to determine whether background task is active.
BOOL isBackgroundTaskRunning;
Then in runBackgroundTask
if (isBackgroundTaskRunning) {
// already running
return;
}
isBackgroundTaskRunning = TRUE;
...
isBackgroundTaskRunning = FALSE;
Here's what to do:
the background task saves its thread to a property somewhere using NSThread currentThread
the background task periodically checks the thread's isCancelled property.
the main thread sends cancel to the thread object saved by the background thread in step 1.
On exit, the background thread sets the property to nil.
All of the operations on the property used to store the thread in have to be protected by #synchronized or equivalent to prevent the main thread from sending cancel to a deallocated thread object.
The background thread can't do IO operations that block for more than a short period of time. In particular, synchronous downloading of URLs using NSURLConnection is out. If you are using NSURLConnection, you'll want to move to the asynchronous methods and a run loop (arguably, in that case, you can do away with the background thread altogether). If you are using POSIX level IO, use poll() with a timeout.
I don't think that it would be save to force the interruption of a method. What you can do is to change the state of your object and check that state inside your method implementation to early return in case of a cancel (but don't forget to release allocated objects).
This is how NSOperationQueue works. From the documentation:
Cancelling an operation does not immediately force it to stop what it is doing. Although respecting the value returned by the isCancelled is expected of all operations, your code must explicitly check the value returned by this method and abort as needed.
Run the method in a background thread, and keep a record of the NSThread. Then later, you can just end the thread.

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
}
}
.
.
.

when is it safe to release an NSThread?

Below is the runloop for my secondary NSThread* processThread
To close the thread I call
//cancel secondary thread
[processThread cancel]
//signal condition
[processCondition broadcast];
Is it then safe to then call:
[processCondition release];
[processThread release];
or do i need to be sure that the thread has finished?
Perhaps like this?
NSTimeInterval timeout = [NSDate timeIntervalSinceReferenceDate] + (1.0/15.0);
while ([processThread isExecuting] && [NSDate timeIntervalSinceReferenceDate] < timeout)
{
[NSThread sleepForTimeInterval: 1.0/1000.0 ];
}
[processCondition release];
[processThread release];
detailed code and explanation:
- (void)processLoop
{
NSAutoreleasePool * outerPool = [[NSAutoreleasePool alloc] init];
[processCondition lock];
//outer loop
//this loop runs until my application exits
while (![[NSThread currentThread] isCancelled])
{
NSAutoreleasePool *middlePool = [[NSAutoreleasePool alloc];
if(processGo)
{
//inner loop
//this loop runs typically for a few seconds
while (processGo && ![[NSThread currentThread] isCancelled])
{
NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc]; init];
//within inner loop
//this takes a fraction of a second
[self doSomething];
[innerPool release];
}
[self tidyThingsUp];
}
else
{
[processCondition wait];
}
[middlePool release];
}
[processCondition unlock];
[outerPool release];
}
the combination of:
an inner while loop
NSCondition *processCondition
toggling processGo between YES and NO
allows me to stop and start the inner while loop without cancelling the thread.
if (processGo == YES)
execution enters the inner while loop.
When the main thread sets
processGo = NO
execution leaves the inner while loop and tidys up
on the next pass of the outer loop, execution hits
[processCondition wait]
and waits
if the the main thread resets
processGo == YES
and calls
[processCondition wait]
execution re-enters the inner loop
Yes, it is safe to call release against an NSThread if you are done with it. In non-GC Objective C code the idiom is that once you are done accessing an object you may release it. If anything else needs that object, including the object itself it their job to have a retain against it. In general if an object cannot be safely disposed at arbitrary times it will retain itself while it is in an unsafe state, and release itself when it can be safely disposed of.
This is how things like NSThread and NSURLConnection work (NSURLConnection actually retains its delegate and does a lot of fancy stuff to cope with the retain loop that occurs.