dispatch_semaphore_wait stops ALL the threads in iPhone APP - iphone

I am using dispatch_semaphore_wait to stop my current thread but it looks like it stops all my threads.
Code:
SampleReader *reader = [[SampleReader alloc] initWithHostname:hostname andFilePath:filepath];
reader.endHandler = endHandler;
[reader start];
dispatch_semaphore_wait(reader->mSem, DISPATCH_TIME_FOREVER);
My start method has something like:
mFileStream = [[NSOutputStream outputStreamToFileAtPath:[fileurl path] append:FALSE] retain];
[mFileStream open];
mNetworkStream = (NSInputStream *)CFReadStreamCreateWithFTPURL(NULL, (CFURLRef)ftpurl);
mNetworkStream.delegate = self;
[mNetworkStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[mNetworkStream open];
I get callback in one of the delegate methods wherein I signal the semaphore
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{
switch (eventCode)
Case NSStreamEventErrorOccurred:
dispatch_semaphore_signal(mSem);
break;
case NSStreamEventEndEncountered:
dispatch_semaphore_signal(mSem);
break;
}
However, when i send wait on semaphore, the delegate method is not called.
Its called only when i comment out the line
//dispatch_semaphore_signal(mSem);
Any help will be greatly appreciated.

If you're calling [reader start] from your main thread then you are creating a deadlock. Your stream is being associated with the main thread here:
[mNetworkStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
That means for it to work the main run loop must be spinning. If the dispatch_semaphore_wait is on the main thread though, you're stopping the run loop and preventing the stream from handling its events.
I don't see how commenting out the dispatch_semaphore_signal would do anything besides make the wait never return.

Related

FTP file using an async method call in iPhone app

I am new to iPhone app development. I want to have a async method that will be called on successful login and work async while I am navigating to various views in the app.
This method should independently work without affecting the main view methods. This method is performing ftp of the files on the local folder to the server.
Could you please tell me or put some sample code which I can refer to. I want to see both for ftp and async method processes.
from my understanding you want to upload something from the iphone to a server in a background thread?
anyway; downloading in a background thread should be quite similar.
first, i suggest you create a method that does the main work for you:
- (void) createRessource {
NSURL *destinationDirURL = [NSURL URLWithString: completePathToTheFileYouWantToUpload];
CFWriteStreamRef writeStreamRef = CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) destinationDirURL);
ftpStream = (__bridge_transfer NSOutputStream *) writeStreamRef;
BOOL success = [ftpStream setProperty: yourFTPUser forKey: (id)kCFStreamPropertyFTPUserName];
if (success) {
NSLog(#"\tsuccessfully set the user name");
}
success = [ftpStream setProperty: passwdForYourFTPUser forKey: (id)kCFStreamPropertyFTPPassword];
if (success) {
NSLog(#"\tsuccessfully set the password");
}
ftpStream.delegate = self;
[ftpStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
// open stream
[ftpStream open];
}
This method is one third of the job: it will be called in the background.
Invoked from something like this:
- (void) backgroundTask {
NSError *error;
done = FALSE;
/*
only 'prepares' the stream for upload
- doesn't actually upload anything until the runloop of this background thread is run!
*/
[self createRessource];
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
do {
if(![currentRunLoop runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]]) {
// log error if the runloop invocation failed
error = [[NSError alloc] initWithDomain: #"org.yourDomain.FTPUpload"
code: 23
userInfo: nil];
}
} while (!done && !error);
// close stream, remove from runloop
[ftpStream close];
[ftpStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
if (error) {
// handle error
}
/* if you want to upload more: put the above code in a lopp or upload the next ressource here or something like that */
}
Now you could call
[self performSelectorInBackground: #selector(backgroundTask) withObject: nil];
and a background thread will be created for you, the stream will be scheduled in its runloop and the runloop is configured and started.
Most important is the starting of the runloop in the background thread - without it, the stream implementation will never start working...
mostly taken from here, where i had a similar task to perform:
upload files in background via ftp on iphone

Wait before returning value iPhone

I found some classes on the internet to establish a tcp connection. The link is in here. I want to customize a class and so far I am able to establish a connection send data and receive data which is great. Once I have created and imported the classes used by the first link that I provided I am able to establish a connection using the following method:
-(void) connectToServerUsingCFStream:(NSString *) urlStr portNo: (uint) portNo {
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
(CFStringRef) urlStr,
portNo,
&readStream,
&writeStream);
if (readStream && writeStream) {
CFReadStreamSetProperty(readStream,
kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream,
kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
iStream = (NSInputStream *)readStream;
[iStream retain];
[iStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[iStream open];
oStream = (NSOutputStream *)writeStream;
[oStream retain];
[oStream setDelegate:self];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream open];
}
}
Since I am going to use this class a lot on my application I am creating my own class and I want to create a -(BOOL) connect{} method. I want to return yes is the connection is establish and no otherwise. The problem is that the way I am able to tell if I establish a connection is by creating a connection attempting to send data and then on the server side I have created a method that whenever I receive that I send a string back. If in the next 2 seconds I receive data I know that the connection was establish.
so in my connection method I want to wait 2 seconds and then return a value depending if the BOOL variable didReciveData = YES.
Since you use a NSOutputStream a better approach could be to check the return value of [oStream write:]: if it returns -1 no data has been sent, so there's no connection.
However, if you want to wait two seconds you can use NSTimer to create a timeout. If you receive a response before the timer fires you can invalidate the timer, otherwise the timer will call the related method that will notify the end of the two seconds.
You could try pausing the main run loop.
- (void)test
{
NSLog(#"Test starting.");
BOOL wasSuccessful = [self connect];
NSLog(#"Success: %d", wasSuccessful);
}
- (BOOL)connect
{
// try to connect here, make sure to get a callback on success/failure
// fake callback
[self performSelector:#selector(callback:) withObject:[NSNumber numberWithBool:NO] afterDelay:2.0];
// wait for callback
CFRunLoopRun();
return self.success;
}
- (void)callback:(NSNumber *)successful
{
self.success = [successful boolValue];
CFRunLoopStop(CFRunLoopGetCurrent()); // now we want -connect to return!
}

NSStreams Crashing Program!

All,
I've run it down to this point by commenting, breakpoints, etc. The program crashes at the marked code.
-(void) initNetworkCommunication
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"192.168.17.1", 2004, &readStream, &writeStream);
inputStream = (NSInputStream *)readStream;
outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];//WHY MUST YOU CRASH HERE
[outputStream open];//WHY MUST YOU CRASH HERE ALSO!!?!?
NSLog(#"She be opened, sir!");
}
It doesn't crash if I comment out both of those, but it crashes if I comment out either one (so i.e. they are both causing the program to crash). There is no information that gets posted in the debugger either. All it does is send me to main.m and show me
"Thread 1: Program received signal: "EXC_BAD_ACCESS".
Thanks for the help in advance!
Edit: Here is my delegate method, but it doesn't even present the second active line in the log.
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %i", streamEvent); //this doesn't post in the log when stream opened...
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream) {
uint8_t buffer[1024];
int len;
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output) {
NSLog(#"server said: %#", output);
//[self messageReceived:output];
}
}
}
}
break;
case NSStreamEventErrorOccurred:
NSLog(#"Can not connect to the host!");
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//[theStream release];
theStream = nil;
break;
default:
NSLog(#"Unknown event");
}
}
What is happening is that the instance of the delegate class is being deallocated (causing EXC_BAD_ACCESS in the run loop) either because you didn't retain it, or you are using ARC (pretty likely) and you do not have a reference to it.
The solution is to either call retain on the delegate class, approximately like so:
SomeStreamDelegate *theDelegate = [[SomeStreamDelegate alloc] init];
[theDelegate retain];
Or if you do have ARC enabled, make an instance variable in the class where you alloc the delegate, and store your instance of connection there. That way ARC will not dealloc it, because the instance var counts as a reference.
If you are using ARC, cast the streams like this :
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
This should prevent the crash. And keep in mind that if your streams are owned by a separate thread than the main thread that means the run loop needs to be called manually using run method after opening the streams.
When I placed this in my View Controller (and not in a separate class) it worked perfectly.
I had a similar issue where my app would crash in the -handleEvent callback with a huge streamEvent number. I resolved it by making sure I initialize the NSStream objects (input and output) AND open a connection to the server in the -init method of the NetworkClient object which my VC plans to use.
Try this out once,
NSInputStream * inputStream = objc_unretainedObject(readStream);
May be a casting issue

Connection doesn't respond when beeing set from a new thread

The connection doesn't respond when beeing set from a new thread:
Code 1 (responds fine):
[self setConnection];
}
- (void)setConnection{
NSLog(#"setting myConnection with request");
myConnection = [[[NSURLConnection alloc]initWithRequest:[NSURLRequest requestWithURL:requestURL] delegate:self] autorelease];
}
Log 1:
2010-02-25 10:44:04.384 Untitled[1002:207] setting myConnection with request
2010-02-25 10:44:06.093 Untitled[1002:207] didReceiveResponse
2010-02-25 10:44:06.094 Untitled[1002:207] didReceiveData
2010-02-25 10:44:06.094 Untitled[1002:207] DidFinishLoading
Code 2:
[NSThread detachNewThreadSelector:#selector(setConnection) toTarget:self withObject:nil];
}
- (void)setConnection{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"setting myConnection with request");
myConnection = [[[NSURLConnection alloc]initWithRequest:[NSURLRequest requestWithURL:requestURL] delegate:self] autorelease];
[pool release];
}
Log 2:
2010-02-25 10:40:50.280 Untitled[972:4003] setting myConnection with request
Delegates:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"didReceiveResponse");
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"didReceiveData");
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"didFailWithError");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"DidFinishLoading");
}
Why is that?
What is the correct way for sending a request & receiving a response- without freezing the main thread / UI.
From the NSURLConnection documentation:
For the connection to work correctly
the calling thread’s run loop must be
operating in the default run loop
mode.
and
Note that these delegate methods will
be called on the thread that started
the asynchronous load operation for
the associated NSURLConnection object.
In your case, what probably happen is the thread is finished before your class actually executed all it’s code.
Read this for a solution on how to start the run loop:
http://www.depl0y.com/2009/02/20/nsurlconnection-in-its-own-thread/
Or, create a second thread and avoid issues of runloops altogether by doing a NSURLConnection sendSynchronousRequest:returningResponse:error, for which no special threading or run loop configuration is necessary in the calling thread.
Do not forget to call back to the main thread via performSelectorInMainThread:
It doesn't work because the thread is finished before your class actually executed all it’s code.
All you need to do now is start the run loop so the thread doesn't exit and the download can work.
[[NSRunLoop currentRunLoop] run];
You can see this mini tuto here: NSURLConnection in it's own thread

NSURLConnection delegation and threading - iPhone

I have a class that updates two .plist files in the app documents directory via an NSURLConnection. The class acts as its own delegate for NSURLConnection. It works properly when I ask for a single file, but fails when I try to update two files. Does it look like I should start a new thread for each of the getNewDatabase messages?
- (void)getAllNewDatabases {
[self performSelectorOnMainThread:#selector(getNewDatabase:) withObject:#"file1" waitUntilDone:YES];
[self performSelectorOnMainThread:#selector(getNewDatabase:) withObject:#"file2" waitUntilDone:YES];
}
- (BOOL)getNewDatabase:(NSString *)dbName
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableString *apiString = [[NSMutableString alloc] initWithString:kAPIHost];
[apiString appendFormat:#"/%#.plist",dbName];
NSURLRequest *myRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:apiString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *myConnection = [[NSURLConnection alloc] initWithRequest:myRequest delegate:self];
[apiString release];
if( myConnection )
{
//omitted for clarity here
}
[pool release];
}
//NSURLConnection delegate methods here ...
I found something interesting with NSURLConnection and NSThread - the thread will only live as long as it takes to perform the method that you call from it.
In the case above the thread will live only as long as getNewDatabase:(NSString *)dbName takes to complete, therefore killing off any of its delegate methods before they actually have time to do anything.
I found this website that gives a better explanation and a solution to the problem
I tweaked it a little bit so I could have a custom time out if it didn't complete in a given time frame (handy when someone is walking around between access points)
start = [NSDate dateWithTimeIntervalSinceNow:3];
while(!isFinished && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]]){
if([start compare:[NSDate date]] == NSOrderedAscending){
isFinished = YES;
}
}
As it stands currently in the code you provided, getNewDatabase: is running on the main thread of your application. The problem in this particular case then is something other than the life cycle of the thread, as James observed in his case.
If you did intend to perform this operation in the background, I'd recommend looking into using NSOperationQueue and NSOperation rather than solving the problem with the current code. I think your case is a great fit for NSOperationQueue, especially given that you have more than one download task to perform.
Dave Dribin has an excellent article about using asynchronous API, such as NSURLConnection, inside an NSOperation. Alternatively, as long as you're running in a background thread, you can also simplify the process and just use a synchronous API method instead in your NSOperation, such as initWithContentsOfURL:.
Marcus Zarra has also written a tutorial that demonstrates how easy it is to incorporate and use NSOperationQueue for simple background operations.