NSThread issue with an iPhone Application
I have a rather complex application at https://github.com/BigBadOwl/iStreamer-Download-Manager which uses Rtmpdump and Ffmpeg libraries to download and convert Flash streams. I would like the thread which does the actual conversion to be released to give the user access to the screen again during the download (for obvious reasons).
Anyway, it runs fine as long as it's in the main thread, but as soon as I try place it on a new thread, the Rtmpdump code crashes. It's a memory thing, but I can't see where I am going wrong. Does anyone mind telling me whats going on?
The part of the code in question is https://github.com/BigBadOwl/iStreamer-Download-Manager/blob/master/iplayerFetch.m
- (void)startTheBackgroundJob:(NSArray *) args {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *thePid = [args objectAtIndex:0];
NSString *flvPath = [args objectAtIndex:1];
NSString *mp4Path = [args objectAtIndex:2];
[self performSelectorOnMainThread:@selector(updateProgressBar:) withObject:flvPath waitUntilDone:NO];
[self getFlashFile:thePid withFlvPathName: flvPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if([fileManager fileExistsAtPath:flvPath]){
NSNumber *filesize = 0;
NSDictionary *fileAttributes = [[NSFileManager defaultManager] fileAttributesAtPath:flvPath traverseLink:NO];
if(fileAttributes != nil){
filesize = [fileAttributes objectForKey:NSFileSize];
}
if([filesize longLongValue] > 1024){
[self removeFlvWrapper:flvPath withMp4PathName:mp4Path];
}
[fileManager removeItemAtPath:flvPath error:NULL];
}
[fileManager release];
[pool drain];}
- (void)beginDownload:(NSString *) thePid withDocumentsFolder:(NSString *) documentsDirectory andTempFolder:(NSString *) tempDirectory {
NSString *flvPath = [NSString stringWithFormat:@"%@/%@.flv", tempDirectory, thePid];
NSString *mp4Path = [NSString stringWithFormat:@"%@/%@.mp4", documentsDirectory, thePid];
NSArray *extraArgs = [[NSArray alloc] initWithObjects:thePid, flvPath, mp4Path, nil];
[NSThread detachNewThreadSelector:@selector(startTheBackgroundJob:) toTarget:self withObject:extraArgs];}
Stacktrace:
#0 getStream (argc=16, argv=0x65a0140) at /Users/colinb/Documents/iStreamer-Download-Manager/rtmpdump.c:746
#1 0x0003c297 in -[iplayerFetch getFlashFile:wi开发者_运维知识库thFlvPathName:] (self=0x658ced0, _cmd=0x58da66, thePid=0x793e8b0, flvPath=0x65994c0) at /Users/colinb/Documents/iStreamer-Download-Manager/iplayerFetch.m:168
#2 0x0003b776 in -[iplayerFetch startTheBackgroundJob:] (self=0x658ced0, _cmd=0x58daca, args=0x6599490) at /Users/bigbadowl/Documents/iStreamer-Download-Manager/iplayerFetch.m:33
#3 0x019a4d4c in -[NSThread main] ()
#4 0x019a4cd8 in __NSThread__main__ ()
#5 0x97ca385d in _pthread_start ()
#6 0x97ca36e2 in thread_start ()
Program received signal: “EXC_BAD_ACCESS”.
When a crash occurs, there will be a backtrace.
Post it.
Either your program will break in the debugger, and the call stack will be in the debugger UI (or you can type 'bt
With that, the cause of the crash is often quite obvious. Without that, we are left to critique the code.
The memory management between threads is very haphazard in the code posted. extraArgs
is being leaked, most likely. You need to release
it in the receiving thread (or in the main thread once you know the receiving thread is done with it). Note that autorelease
cannot be used to transfer ownership of an object from one thread to another.
Given the lack of a crash and the claim that the code works fine on main thread, but not when threaded, my best guess is that you are abusing a non-threadsafe API from multiple threads, potentially also triggering UI updates from outside the main thread.
Namely, is removeFlvWrapper:*
thread safe? Remember that the Foundation collection classes are not thread safe.
Is your [self getFlashFile:thePid withFlvPathName: flvPath];
method thread safe? What about the function it calls? Is this being fired up only once or do you have multiple threads?
It's often best to use a process of elimination to find these types of bugs.
When running in the main thread, -updateProgressBar:
won't run until the download has completed. You should remove it until you've isolated the problem.
Also, NSFileManager
instances are not thread safe, so you cannot use [NSFileManger defaultManager]
from your background thread. Instead, you must create a new instance of NSFileManager to be used from your background thread. e.g., [[NSFileManager alloc] init];
You should also try to capture the crash while stepping through the code. This may give a better idea of where the problem is. The line number reported in the callstack (rtmpdump.c:746) doesn't make much sense. You should try to get a better idea of where the crash is occurring. Does it occur on the same line every time, or does it vary? And which lines?
Also, why are these declared static?
static char **rt_argv;
static int rt_argc;
Declaring them static here makes them global variables, which will absolutely cause fatal problems when you try to run multiple download threads.
精彩评论