Marvelous! Thank you very much, Ken. All of your advices worked like a charm.
As always, it's been a good idea to get rid of thread spawning. I've done so using the asynchronous readToEndOfFileInBackgroundAndNotify method of the reading file handle of the pipe. It is indeed that I used the synchronous readDataToEndOfFile method before, my main thread was blocked. It has been also the reason why the NSTaskDidTerminateNotification did not arrive. Anyway, I don't actually need this notification, but instead I used NSFileHandleReadToEndOfFileCompletionNotification. So the problem is solved for now, but one question still left open for the future is the following: would it be thread-safe to use NSTask in the way I originally attempted? If yes, why did it leak? The leak was reported somewhere inside NSTask internal initialization code. Funny enough, the leak disappeared if I made one superfluous -release call on NSTask. On Wed, Feb 4, 2009 at 1:30 PM, Ken Thomases <k...@codeweavers.com> wrote: > On Feb 4, 2009, at 4:49 AM, Oleg Krupnov wrote: > >> I use an NSTask to collect system info from system_profiler. Because >> it takes a while to complete, I launch that task in a separate thread, >> to avoid UI from freezing. > > Both NSTask and NSFileHandle provide asynchronous interfaces, so it is not > necessary to use a separate thread. > > >> - (void)systemProfilerThread:(id)ignored >> { >> NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; >> >> NSString *configuration = nil; >> >> NSPipe* inputPipe = [NSPipe pipe]; > > You do nothing with inputPipe, so you should just get rid of it. If you > want the task to not inherit your standard input, do [scriptTask > setStandardInput:[NSFileHandle fileHandleWithNullDevice]]. > >> NSPipe* outputPipe = [NSPipe pipe]; >> >> NSTask* scriptTask = [[[NSTask alloc] init] autorelease]; >> >> [scriptTask setLaunchPath:@"/usr/sbin/system_profiler"]; >> [scriptTask setArguments:[NSArray arrayWithObjects:@"-detailLevel", >> @"mini", nil]]; >> [scriptTask setStandardOutput:outputPipe]; >> [scriptTask launch]; >> >> [[inputPipe fileHandleForWriting] closeFile]; >> configuration = [[[NSString alloc] initWithData:[[outputPipe >> fileHandleForReading] readDataToEndOfFile] >> >> encoding:NSUTF8StringEncoding] autorelease]; >> if (!m_isCanceled) >> { >> [m_target performSelectorOnMainThread:m_action >> withObject:configuration waitUntilDone:NO]; >> } >> >> [pool drain]; >> } >> >> The problem is, according to the Leaks tool, is that there's a memory >> leak in -systemProfilerThread. The leak disappears if I launch the >> task in the main thread. > > You don't say what is leaking or where it was allocated from. > > >> I've read in docs that NSTask is NOT thread safe, but does it also >> apply to the above case, when the NSTask object is created and >> released within a single thread, though not the main thread? If NSTask >> cannot be used this way, then which way I should use it? > > Create and launch the task from the main thread. Register for the task > termination notification, if you're interested. > > Also register for notification of read-to-end-of-file completion on the > output file handle, and invoke -readToEndOfFileInBackgroundAndNotify on it. > In the handler for that notification is where you can decode/parse the > data. > > After you have registered for these notifications and launched the task, > return immediately. Don't block waiting for either the task to terminate or > data to arrive from the file handle. > > >> Also, what puzzles me is why, first and foremost, the main thread is >> blocked until the task is complete? I do not call wait -waitUntilExit. >> I would expect that the main thread is not blocked, but I receive the >> NSTaskDidTerminateNotification, but it does not happens with the code >> above. > > I assume you mean the main thread is blocked if you _don't_ detach a > separate thread, right? In that case, it's because you're using > -readDataToEndOfFile, which is synchronous. It doesn't return until it has > read to the end of the output from system_profiler -- that is, when > system_profiler terminates and closes its standard output. > > Also, where are you registering for NSTaskDidTerminateNotification? I don't > see it above. In any case, regardless of on which thread you register for > that notification, I would expect it to be posted on the thread which > created the NSTask. Although notifications are not normally passed through > the run loop, this particular notification is the result of the framework > "noticiing" that the task has completed (an event which is, of course, > external to your program and the framework's direct knowledge). In order to > notice that, the framework needs the run loop of the thread to run. You > never run the run loop for your thread in the code above. > > I suspect, in fact, that that's the cause of the memory leak. The framework > had some housekeeping to do that relies on the run loop running. > > Cheers, > Ken > > _______________________________________________ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com