Cause NSThread can't be joinable I tried next method, it seems works ok, but is still very bad solution or good enough?
// init thread
NSThread *mythread = [[NSThread alloc] initWithTarget:self selector:#selector(runThread:) object: nil];
// start thread
mythread.start;
// JOIN NSThread custom implementation
// wait until thread will finish execution
if (mythread.isExecuting) {
while(mythread.isExecuting) {
sleep(0);
}
} else if (!mythread.isCancelled && !mythread.isFinished) {
while(!mythread.isExecuting) {
sleep(0);
}
while(mythread.isExecuting) {
sleep(0);
}
}
A live lock like this is a bad idea on an iPhone, because it eats battery and CPU without doing anything, even though calling sleep(0) might give it a little bit of rest.
You could use NSCondition to implement joining. The idea is that the parent thread will wait on the NSCondition, and the worker thread would signal on that condition when it finishes:
- (void)main1 {
// thread 1: start up
_joinCond = [NSCondition new];
[mythread start];
// thread 1: join, i.e. wait until thread 2 finishes
[_joinCond lock];
[_joinCond wait];
[_joinCond unlock];
}
- (void)main2 {
// thread 2 (mythread):
// ... work, work, work ...
// now we're done, notify:
[_joinCond lock];
[_joinCond signal];
[_joinCond unlock];
}
Related
I'm curious whether it is safe to finish custom NSOperation on different thread that their origin thread e.g.:
I have my custom operation class which is executed on different thread let say thread B (not main thread), then in this operation class I have obviously start() method, where on very beginning I'm invoking [self markAsExecuting]; method to indicate that operation already starts their work and of course after some stuff I have to invoke [self markAsFinished]; to indicate that all work has been done and operation is finished.
My question is: whether it is safe to invoke [self markAsFinished]; method on different thread that my operation itself is execute let say thread C?
Some snipped code:
- (void)start {
#autoreleasepool {
// *** Thread B
[self markAsExecuting];
[apiManager fetchData completion:^(NSDictionary *data, NSError *error) {
if (error == nil) {
// As we know in this case when we do not indicate that AFNetworking response handle should be executed on different thread by default it will be executed on main thread, so that why I'm dispatching expensive work to the background
self.queue = dispatch_queue_create("com.something.myapp.backgroundQueue", 0);
dispatch_async(self.queue, ^{
// Some expensive work
// *** Thread C
[weakSelf markAsFinished];
});
} else {
// *** Main Thread
[weakSelf markAsFinished];
}
}];
}
}
I'm hope that my problem explanation was clear enough.
My question was maybe not precisely enough, markAsFinish method only set the Operation state to .finish so it doesn't make big different from which thread it is called in this case
I have developed the following method, which checks the app's ability to communicate with the server.
The method performs a simple query and knows that if it gets a result, the app should be connected (basic ping mechanism).
- (BOOL)isAppConnected
{
__block BOOL isConnected = NO;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[[SFRestAPI sharedInstance] performSOQLQuery:#"SELECT id FROM Account LIMIT 1"
failBlock:^(NSError *e) {
isConnected = NO;
NSLog(#"NOT CONNECTED %#", e);
NSLog(#"fail block ON THE MAIN THREAD? %hhd", [NSThread isMainThread]);
dispatch_semaphore_signal(semaphore);
} completeBlock:^(NSDictionary *dict) {
isConnected = YES;
NSLog(#"%#", dict);
NSLog(#"complete block ON THE MAIN THREAD? %hhd", [NSThread isMainThread]);
dispatch_semaphore_signal(semaphore);
}];
// if the wait times-out we will receive a non-zero result and can assume no connection to SF
//When using: DISPATCH_TIME_FOREVER the app hangs forever!!
int waitResult = dispatch_semaphore_wait(semaphore, 30 * NSEC_PER_SEC);
NSLog(#"waitResult: %d", waitResult);
return isConnected;
}
I am using the 'dispatch_semaphore_wait' as suggested in the Apple documentation
My goal is to wait on the response or a short timeout to figure out if we really have a valid connection.
With the code above, 'dispatch_semaphore_wait' never actually waits, i.e. execution does not stop at that line but it continues immediately (always returning 49 as the result to the dispatch_semaphore_wait call). That is unless I use DISPATCH_TIME_FOREVER in which case the app hangs forever...
At the moment I am calling this method from the main thread. I am aware that this is a bad idea, but I wanted to see this working as expected before refactoring.
What could be causing this behaviour?
Thanks.
The parameter for dispatch_semaphore_wait is not a delay, but the time when the semaphore should wake up. Yours will wake up 30 seconds after Midnight, Jan 1st. 1970 (or 2001, not sure). Use the dispatch_time function.
- (BOOL)isAppConnected
{
__block BOOL isConnected = NO;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// Add this code...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[SFRestAPI sharedInstance] performSOQLQuery:#"SELECT id FROM Account LIMIT 1"
failBlock:^(NSError *e) {
isConnected = NO;
NSLog(#"NOT CONNECTED %#", e);
NSLog(#"fail block ON THE MAIN THREAD? %hhd", [NSThread isMainThread]);
dispatch_semaphore_signal(semaphore);
} completeBlock:^(NSDictionary *dict) {
isConnected = YES;
NSLog(#"%#", dict);
NSLog(#"complete block ON THE MAIN THREAD? %hhd", [NSThread isMainThread]);
dispatch_semaphore_signal(semaphore);
}];
});
int waitResult = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(#"waitResult: %d", waitResult);
return isConnected;
}
First of all if your fail and complete block are invoked on the main thread then you are going to wait forever, or until the timeout you specify 'times out'
The reason is because the main thread starts waiting after you call dispatch_semaphore_wait. And then if your performSOQLQuery calls the blocks on the main thread nothing will happen until the time out 'times out'.
Now of coarse if you specify a time out of forever the semaphore will never signal or let go, which means your main thread will wait forever, for itself.
Change the waiting code to this: never make the main thread wait
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
int waitResult = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(#"waitResult: %d", waitResult);
});
And do not return a Bool as you are doing, since this is a lengthy operation, you want to use a block that has a result.
Also make sure that your performSOQLQuery() method is not on the main thread please.
I have an implementation of Core Data that uses parent contexts.
I have a main context that runs on main queue and a back context that runs on a private queue and the main context is his parent.
I override the context save method with the following:
- (BOOL)save:(NSError **)error {
BOOL retVal = YES;
if (self.parentContext) {
//push changes to parent context.
retVal = [super save:error];
if (retVal) {
//save parent context.
[self.parentContext performBlock:^{
[self.parentContext save:error];
}];
}
} else {
//save changes.
retVal = [super save:error];
}
return retVal;
}
My problem is when my main queue calls:
- (BOOL)doSomething {
__block BOOL retVal;
dispatch_sync(workerQueue, ^{
retVal = [super doSomething];
});
return retVal;
}
if [super doSomething] has a call to the context save method I get a deadlock (the dead lock is in the line: retVal = [super save:error]; in my save method).
Here is the stack trace for the worker queue:
0 semaphore_wait_trap
1_dispatch_thread_semaphore_wait$VARIANT$mp
2_dispatch_barrier_sync_f_slow
3_perform
4-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error]
5 -[NSManagedObjectContext save:]
Here is the stack trace for the main queue:
0 semaphore_wait_trap
1 _dispatch_thread_semaphore_wait$VARIANT$mp
2 _dispatch_barrier_sync_f_slow
3 [Service doSomething]
for some strange reason this happens only on iPad and not iPhone
Is there a way to overcome this deadlock using recursive locks?
You must not use your own dispatch queues for Core Data operations. Use performBlock:
or performBlockAndWait: to ensure that the operation is executed on the queue
associated with the managed object context.
See "Concurrency" in the NSManagedObjectContext Class Reference.
I needed to sync some background threads I got in my iOS app. I needed something to hold the execution of one thread while the other did its job, in order to keep the execution of the code linear.
I've been fighting with this code all day, and after a lot of reading I came up with this code:
-(void)waitOne:(double)timeout
{
if (!shouldRun)
{
shouldRun = true;
double runs = 0;
do {
NSDate* next = [NSDate dateWithTimeIntervalSinceNow:0.5];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:next];
runs++;
} while ((runs/2) < timeout && shouldRun);
}
else
{
#throw [NSException exceptionWithName:#"InvalidHandle" reason:#"The currenct handle is in use" userInfo:nil];
}
}
I can tell you that it got the job done. But I also found some bad feedback at the forum where I found the idea for that algorithm, saying that "it'll make your loop busy wait" and that was a bad idea.
So, I come here to ask. Is there a better way to accomplish what I want?
Look into [NSCondition] which enables you to wait and signal threads
Basically you allocate a NSCondition and in the block you'll have [condition wait]; which will cause the thread to wait. then, when the async operation is done, you call [condition signal]; which will signal the waiting thread to continue.
http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/NSCondition_class/Reference/Reference.html
For future reference, if anyone needs, here's how my code ended up:
#implementation WaitHandle
#synthesize isWaiting;
-(void)waitOne:(double)timeout
{
if (!isWaiting)
{
isWaiting = true;
if (timeout <= 0) {
timeout = 0.1;
}
[handle waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeout]];
}
}
-(void)signal
{
[handle signal];
}
-(id)init
{
self = [super init];
if (self != nil)
{
isWaiting = false;
handle = [[NSCondition alloc] init];
}
return self;
}
-(void)dealloc
{
[self signal];
}
#end
Very simple code:
queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(#"%#", [NSThread mainThread]? #"main" : #"not main");
}];
prints "main".
Why? Isn't it suppose to run in bg thread asynchronously, unless i call [NSOperationQueue mainQueue] ?
[NSThread mainThread] always returns an object (and thus yielding YES when cast to BOOL), since there is a main thread when your program is running.
If you want to check whether the current thread is the main thread or not, you need to use the currentThread method of NSThread.
NSLog(#"%#", [[NSThread currentThread] isEqual:[NSThread mainThread]]
? #"main" : #"not main");
NSThread has a better method; it seems you can use the isMainThread method to check whether the current thread is the main thread or not:
if ([[NSThread currentThread] isMainThread]) {
//
}
Well as user #borrrden pointed out, you just need to use [NSThread isMainThread],
if([NSThread isMainThread]){
//
}
See the NSThread documentation.