iphone sdk communicate between thread - objective-c

My application has a second running thread. I need to achieve the following :
Stop the separate thread gracefully from the main application thread
Call a function on the main thread from the second thread to signal a result has been found and pass it to the main one.
I've found the following for the first task : share a global variable between the 2 threads ?
No idea how to achieve the second task. (NSNotificationCenter doesn't allow to pass objects ...)
I'm lunching the second thread like this [NSThread detachNewThreadSelector:#selector(backGroudTask) toTarget:self withObject:nil];
Thanks

I'm still searching for the best answer to this, but here is what I do:
Use NSLock to create a lock that prevents me from accessing the same variable on both threads. Then use a BOOL to see if the main thread wants to initiate a stop.
in main thread do this
[myLock lock];
exitFlag = YES;
[myLock unlock];
in the other thread do this
endMe = NO;
while(!endMe)
{
// do your task stuff
[myLock lock];
endMe = exitFlag;
[myLock unlock];
}
For the second part of your question use the following:
[self performSelectorOnMainThread:#selector(your_selector_name) withObject:nil waitUntilDone:false];
This will cause the your selector routine to run on the main thread.
Hope this helps

(NSNotificationCenter doesn't allow to pass objects ...)
it does, but you have to add them to the userinfo of the notification
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:myObject forKey:#"object"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification" object:self userInfo:userInfo];
- (void)foo:(NSNotification *)notification {
id object = [[notification userInfo] objectForKey:#"object"];
}

Related

Is it possible to check if an NSThread is blocked?

I've always been interested in how to write the following code to use it for unit testing:
Is it possible to extend NSThread with a method that would check if a particular thread is blocked?
Right now I'am working with NSCondition: Xcode shows me the chain which is called by -wait to block the thread:
[NSCondition wait]
pthread_cond_wait$UNIX2003
_pthread_cond_wait
__psynch_cvwait
Besides checking the locks done by NSCondition, if it is even possible, I would highly appreciate method working also for any other blocking capabilities (dispatch semaphores, condition locks, sleeping threads and so on, ) - I have no idea about Objective-C internals, if maybe they could be catched by one method or each needs its own.
Here is a simple example of what I would like to achieve. The mysterious method is called isBlocked.
// Some test case
// ...
__block NSThread *thread;
NSCondition *condition = [NSCondition alloc] init];
dispatch_async(someQueue(), ^{
thread = NSThread.currentThread;
[condition lock];
[condition wait];
[condition unlock];
});
while(1) {
NSLog(#"Thread is blocked: %d", thread.isBlocked);
}
Note: I am not good at C and all this low-level POSIX stuff, so, please, be verbose.
Note 2: I am interested in solutions working for dispatch queues as well: if someone can show me how to test the fact that someQueue() is blocked by -[NSCondition wait] (not the fact that it is going to be blocked (fx hacking some code before -[condition wait] is run and the block is set), but the fact that thread/queue is blocked), I will accept this as an answer as much like I would do with working -[NSThread isBlocked] method.
Note 3: Suspecting bad news like "it is not possible", I claim that any ideas about catching the fact that -[condition wait] was run and the thread was set blocked (see Note 2) are appreciated and can be also accepted as an answer!
UPDATE 1 in address to the nice answer by Richard J. Ross III. Unfortunately, his answer does not work in my original example, the version which is closer to my real work (though it does not differ much from the example I've initially provided - sorry that I didn't include it in the first edition of the question):
// Example
// Here I've bootstrapped Richard's isLocking categories for both NSThread and NSCondition
// ...
// somewhere in SenTesting test case...
__block NSThread *thread;
NSCondition *condition = [NSCondition alloc] init];
__block BOOL wePassedBlocking = NO;
dispatch_async(someQueue(), ^{
thread = NSThread.currentThread;
[condition lock];
[condition wait];
[condition unlock];
wePassedBlocking = YES; // (*) This line is occasionally never reached!
});
while(!thread.isWaitingOnCondition); // I want this loop to exit after the condition really locks someQueue() and _thread_ __.
// sleep(1);
[condition lock];
[condition broadcast]; // BUT SOMETIMES this line is called before -[condition wait] is called inside someQueue() so the entire test case becomes blocked!
[condition unlock];
while(!wePassedBlocking); // (*) And so this loop occasionally never ends!
If I uncomment sleep(1) test begins working very stable without any occasional locks!
This leads us to the problem, that Richard's category does set state exactly one line before the actual blocking is done meaning that sometimes test case's main thread catches this new state before we actually have someQueue/thread blocked because Richard's code does not contain any synchronization mechanisms: #synchronized, NSLock or something like that! I hope I am making a clear explanation of this tricky case. For anyone who has doubts about what I've posted here, I would say that I have been also experimenting with multiple queues and even more complex cases, and if needed I'm ready to provide more examples. Richard, thanks again for your effort, let's think more together, if you understand these my points!
UPDATE 2
I see the dead-end paradox: obviously, to really set the state of waitingOnCondition we need to wrap this state's change inside some synchronization closures, but the problem is that the closing one, unlocking the synchronization lock, should be called after -[condition wait], but it can't, because the thread is already blocked. Again, I hope I am describing it pretty clear.
Here you go! It won't detect threads being waited on by anything other than -[NSCondition wait], but it could easily be extended to detect other kinds of waiting.
It's probably not the best implementation out there, but it does in fact work, and will do what you need it to.
#import <objc/runtime.h>
#implementation NSThread(isLocking)
static int waiting_condition_key;
-(BOOL) isWaitingOnCondition {
// here, we sleep for a microsecond (1 millionth of a second) so that the
// other thread can catch up, and actually call 'wait'. This time
// interval is so small that you will never notice it in an actual
// application, it's just here because of how multithreaded
// applications work.
usleep(1);
BOOL val = [objc_getAssociatedObject(self, &waiting_condition_key) boolValue];
// sleep before and after so it works on both edges
usleep(1);
return val;
}
-(void) setIsWaitingOnCondition:(BOOL) value {
objc_setAssociatedObject(self, &waiting_condition_key, #(value), OBJC_ASSOCIATION_RETAIN);
}
#end
#implementation NSCondition(isLocking)
+(void) load {
Method old = class_getInstanceMethod(self, #selector(wait));
Method new = class_getInstanceMethod(self, #selector(_wait));
method_exchangeImplementations(old, new);
}
-(void) _wait {
// this is the replacement for the original wait method
[[NSThread currentThread] setIsWaitingOnCondition:YES];
// call the original implementation, which now resides in the same name as this method
[self _wait];
[[NSThread currentThread] setIsWaitingOnCondition:NO];
}
#end
int main()
{
__block NSCondition *condition = [NSCondition new];
NSThread *otherThread = [[NSThread alloc] initWithTarget:^{
NSLog(#"Thread started");
[condition lock];
[condition wait];
[condition unlock];
NSLog(#"Thread ended");
} selector:#selector(invoke) object:nil];
[otherThread start];
while (![otherThread isWaitingOnCondition]);
[condition lock];
[condition signal];
[condition unlock];
NSLog(#"%i", [otherThread isWaitingOnCondition]);
}
Output:
2013-03-20 10:43:01.422 TestProj[11354:1803] Thread started
2013-03-20 10:43:01.424 TestProj[11354:1803] Thread ended
2013-03-20 10:43:01.425 TestProj[11354:303] 0
Here is a solution using dispatch_semaphore_t
PGFoo.h
#import <Foundation/Foundation.h>
#interface PGFoo : NSObject
- (void)longRunningAsynchronousMethod:(void (^)(NSInteger result))completion;
#end
PGFoo.m
#import "PGFoo.h"
#implementation PGFoo
- (void)longRunningAsynchronousMethod:(void (^)(NSInteger))completion {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(5);
completion(1);
});
}
#end
Test Methods
- (void)testThatFailsBecauseItIsImpatient {
PGFoo *foo = [[PGFoo alloc] init];
__block NSInteger theResult = 0;
[foo longRunningAsynchronousMethod:^(NSInteger result) {
theResult = result;
}];
STAssertEquals(theResult, 1, nil);
}
- (void)testThatPassesBecauseItIsPatient {
PGFoo *foo = [[PGFoo alloc] init];
__block NSInteger theResult = 0;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[foo longRunningAsynchronousMethod:^(NSInteger result) {
theResult = result;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
STAssertEquals(theResult, 1, nil);
}
By using a dispatch_semaphore_t you can "track" whether a thread that is waiting on that semaphore is blocked. For every call of dispatch_semaphore_wait the semaphore's count is decremented and the thread waits until a call of dispatch_semaphore_signal is made, when dispatch_semaphore_signal is called the semaphore's count is incremented, if the count is incremented to a value greater than -1 the thread continues.
This solution fails to answer your question about checking whether an NSThread is "blocked" but I think it provides what you are reaching for, assuming you're not reaching to check on NSThread instances that are maintained within an existing framework.

performBlockAndWait creates deadlock

I am writing a function that performs some CoreData stuff. I want the function to return only after all the CoreData operations have executed. The CoreData stuff involves creating an object in a background context, then doing some more stuff in the parent context:
+ (void) myFunction
NSManagedObjectContext *backgroundContext = [DatabaseDelegate sharedDelegate].backgroundContext;
[backgroundContext performBlockAndWait:^{
MyObject *bla = create_my_object_in:backgroundContext;
[backgroundContext obtainPermanentIDsForObjects:[[backgroundContext insertedObjects] allObjects] error:nil];
[backgroundContext save:nil];
[[DatabaseDelegate sharedDelegate].parent.managedObjectContext performBlockAndWait:^{
[[DatabaseDelegate sharedDelegate].parent updateChangeCount:UIDocumentChangeDone];
// Do some more stuff
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:someOperation];
}];
}];
return;
}
I want the return to only happen after [queue addOperation:someOperation].
This seems to work most of the cases, but I have had one case when this function never returned. It seemed like it was deadlocked, and I suspect it is because of performBlockAndWait.
My questions are:
(1) Can someone explain why this deadlock occurs?
and
(2) What is the right way of achieving the same functionality? The requirement is that myFunction returns only after both blocks have been executed.
Thank you!
Let's imagine you are calling myFunction from the main thread. Let's imagine [DatabaseDelegate sharedDelegate].parent.managedObjectContext is scheduled on the main thread.
With [backgroundContext performBlockAndWait:] you are scheduling a block on the context private background queue. Blocking the main thread.
With [.parent.managedObjectContext performBlockAndWait:], you are scheduling a block on the main thread, blocking the private queue.
But the main thread is blocked already. So the block will never execute. And performBlockAndWait: will never returns.
Deadlock.
Use asynchronously scheduled blocks, with completion blocks.
You don't have to wait. Your background work executes, then, before it is done, it kicks off work on the main thread, and before it is done, it does your "someOperation." You could replace it with async and it will still work.
Looking at this code, there is no reason to use the blocking versions...
+ (void) myFunction {
NSManagedObjectContext *backgroundContext = [DatabaseDelegate sharedDelegate].backgroundContext;
[backgroundContext performBlock:^{
// Asynchronous... but every command in this block will run before this
// block returns...
MyObject *bla = create_my_object_in:backgroundContext;
[backgroundContext obtainPermanentIDsForObjects:[[backgroundContext insertedObjects] allObjects] error:nil];
[backgroundContext save:nil];
[[DatabaseDelegate sharedDelegate].parent.managedObjectContext performBlock:^{
// Asynchronous, but this whole block will execute...
[[DatabaseDelegate sharedDelegate].parent updateChangeCount:UIDocumentChangeDone];
// Do some more stuff
// This will not run until after the stuff above in this block runs...
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:someOperation];
}];
// You will reach here BEFORE the code in the previous block executes, but
// the "someOperation" is in that block, so it will not run until that
// block is done.
}];
// Likewise, you will reach here before the above work is done, but everything
// will still happen in the right order relative to each other.
return;
}

Managed Object Context not saving to persistant store

I have a threaded operation that creates a new managed object, saves it to the persistant store, then passes the objectID of the new objected via NSNotification to the main thread for further processing
However, when I try to access the newly created managed object from the main thread all the values that I set on the background thread return as empty.
** background thread
// create a new managed object context for this thread
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
[context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
// create the object
MyObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:#"MyObject" inManagedObjectContext:context];
[newManagedObject setAValue:#"A"];
[newManagedObject setBValue:#"B"];
[newManagedObject setCValue:#"C"];
// save it on the main thread
[context performSelectorOnMainThread:#selector(save:) withObject:nil waitUntilDone:NO];
// post notification to main thread, pass the objectID
NSMutableDictionary *userInfo = [NSDictionary dictionaryWithObject:[newManagedObject objectID] forKey:#"objectID"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"doneInsertingObject" object:userInfo];
[context release];
** main thread
...
// register notification for background thread
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(mergeContextChanges:) name:NSManagedObjectContextDidSaveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doSomethingWithObject:) name:#"doneInsertingObject" object:nil];
...
- (void)doSomethingWithObject:(NSNotification*)noif
{
if([NSThread isMainThread] == NO)
{
// run this on the main thread
[self performSelectorOnMainThread:_cmd withObject:noif waitUntilDone:NO];
return;
}
// get managed object from objectID
NSDictionary *userInfo = [noif userInfo];
MyObject *object = (MyObject*)[appDelegate.managedObjectContext objectWithID:[userInfo valueForKey:#"objectID"]];
[appDelegate.managedObjectContext refreshObject:object mergeChanges:YES];
// these should return 'A, B, C' but all three return 'nil'
NSLog(#"aValue: %#", object.aValue);
NSLog(#"bValue: %#", object.bValue);
NSLog(#"cValue: %#", object.cValue);
}
// merge background thread moc with main moc
- (void)mergeContextChanges:(NSNotification *)notification
{
if([NSThread isMainThread] == NO)
{
// run this on the main thread
[self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
return;
}
// fault all updated objects
NSSet *updated = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];
for(NSManagedObject *thing in updated)
{
[[appDelegate.managedObjectContext objectWithID:[thing objectID]] willAccessValueForKey:nil];
}
// merge changes to the main managed object context
[appDelegate.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
// force processing of any pending changes
[appDelegate.managedObjectContext processPendingChanges];
}
I've tried changing merge policies and there was no difference.
I've tried adding logging to the context merge method and I have confirmed receiving a "inserted" notification from the background thread before the doSomethingWithObject: method on the main thread is called.
Why is my data not being updated to the persistant store?
I can't see where you save the context for your background thread. If it's this line
// save it on the main thread
[context performSelectorOnMainThread:#selector(save:) withObject:nil waitUntilDone:NO];
I don't know if it is correct. You have save the context from the thread that has created it and not in the main thread.
[context save:&error];
For further info, I suggest you to read the articles by Marcus Zarra importing-and-displaying-large-data-sets-in-core-data. You can find the sample code at the end. In addition you can find further info in using-core-data-on-multiple-threads.
Hope it helps.
Your NSManagedObjectContext has to be saved on the thread it was created on (as #Flex_Addicted pondered).
After it has saved on the background thread, a notification will be posted telling you to merge changes from the background context to the main context.
Apples documents read "Saving in a Background Thread is Error-prone" - this isn't related to using another NSManagedObjectContext. They are saying that if you have 1 context, and you try to dispatch the save operation to the background - THAT is error prone. If you have multiple contexts, they should only be used within one thread at a time.

Simple NSThread or NSTimer

I want to run certain background tasks.
Scenario: I would like a button to activate a thread or timer, and then have the thread/timer to start repeating every second returning a NSRunInformationalAlertPanel to the user with data.
This is what I have for my timer:
-(void)workerThread:(NSTimer*) theTimer {
if(intNumberOfTicks > 0)
{
NSRunInformationalAlertPanel(#"The Serial", [NSString stringWithFormat:#"%d", intNumberOfTicks], #"OK", nil, nil);
//[txtTimeMinutes setStringValue:[NSString stringWithFormat:#"%d", intNumberOfTicks]];
intNumberOfTicks--;
}
else {
[timer invalidate];
}
}
And for starting the method...
intNumberOfTicks = 5;
timer = [[NSTimer scheduledTimerWithTimeInterval:1 target: self selector:#selector(workerThread:) userInfo:self repeats:true] retain];
// Or for threading...
///[NSThread detachNewThreadSelector:#selector(workerThread) toTarget:self withObject:nil];
Can anyone help me implement what I need, maybe providing the most basic examples for a NSThread or NSTimer. I have looked at the Apple Dev Refrences but no luck.
Using NSTimer will execute the selector in the same thread as the one which instantiated and invoked it.
If your task must be carried out in a background thread try calling performSelectorInBackground:withObject:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/performSelectorInBackground:withObject:
From that background thread you can use a scheduled timer the way that you described above.

How to join threads in Objective C without using delegates/callback?

Is there a clean way of joining threads in Objective C much like "Thread.join" in Java? I found the method performSelector:onThread:withObject:waitUntilDone: but the limitation of this is I can't call the "blocking" on a different line because I want to do something like this:
[dispatch Thread A];
[process something on main thread];
[wait for Thread A to finish before proceeding];
Thank you in advance.
I'm not aware of any Cocoa API to do this, but it wouldn't be too difficult to do with NSThread, pretty easy to do with a lock, and even easier to do with Grand Central Dispatch.
NSThread
NSThread * otherThread = [[NSThread alloc] initWithTarget:self selector:#selector(methodToPerformInBackground:) object:aParameter];
[otherThread start];
//do some stuff
while ([otherThread isFinished] == NO) {
usleep(1000);
}
[otherThread release];
NSLock
NSLock * lock = [[NSLock alloc] init];
//initiate the background task, which should immediately lock the lock and unlock when done
//do some stuff
[lock lock]; //this will pause until the background stuff unlocks
[lock unlock];
[lock release];
Grand Central Dispatch
dispatch_group_t myGroup = dispatch_group_create();
dispatch_group_async(myGroup, dispatch_get_global_queue(), ^{
//stuff to do in the background
});
//do some stuff
dispatch_group_wait(myGroup, DISPATCH_TIME_FOREVER);
dispatch_release(myGroup);
NSConditionLock is the answer to my question, Sorry Dave DeLong, but I cannot use:
"while ([otherThread isFinished] == NO) "
-- because I need fast continuous processing and cannot use sleep.
NSLock
-- because as you said it "initiate the background task, which should immediately lock the lock and unlock when done", this is not a solution because I tried it and we are not sure if the subthread will execute last before the lock-unlock-release on main thread, I ended up getting random errors.
Grand Central Dispatch
--because it's only available in IOS4 and Snow Leopard 10.6, I'm using a lower version.
But your answer gave me the idea and thank you very much for it, so I just "upped" you.
I ended up doing this:
#define T_START 0
#define T_FINISHED 1
-(void) updateVerticalScopeBackground: (id) aParam {
[lockForThread lock];
NSAutoreleasePool *pool = [NSAutoreleasePool new];
//do something
[pool release];
[lockForThread unlockWithCondition:T_FINISHED];
}
-(void) sumFunc {
lockForThread = [[NSConditionLock alloc]
initWithCondition: T_START];
NSThread* updateVerticalScope = [[NSThread alloc] initWithTarget:self selector:#selector(updateVerticalScopeBackground:) object:nil];
[updateVerticalScope start];
//do some processing
[lockForThread lockWhenCondition:T_FINISHED];
[lockForThread unlockWithCondition:T_FINISHED];
[lockForThread release];
}
You could use NSCondition signal/wait.
Could you use a lock to do this? In other words something like this (pseudocode)
create an object to lock on, visible to both threads
dispatch thread A; thread A immediately takes the lock and keeps it for its duration
process something on main thread
main thread attempts to take the lock (this will block until Thread A releases it)
after acquiring the lock, main thread releases it and continues on
You never want your main thread to be blocked waiting for another thread. At least you don't in any application with a user interface because, if the main thread is blocked, your application is frozen.
It would be far better for the main thread to start the background thread, do the other stuff it needs to do and then return to the run loop. The background thread would notify the main thread of completion by sending -performSelectorOnMainThread:waitUntilDone: