0

I am trying to use a NSInputStream inside a concurrent NSOperation.

I noticed that I can not schedule it on the current run loop, only the main run loop. Why is this? Do I need to create my own run loop for the thread the NSOperation is on?

Here is the code:

{
    NSURL *filePath = [Utils getFullFileURLFromFileId:self.fileId];
    NSString *filePathString = [filePath path];
    self.iStream = [[NSInputStream alloc] initWithFileAtPath:filePathString];
    self.iStream.delegate = self;
    [self.iStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    [self.iStream open];

}

#pragma mark - NSStreamDelegate

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
// do stuff
 }
user1028028
  • 6,323
  • 9
  • 34
  • 59

2 Answers2

2

Make sure that the run loop you schedule the stream on is actually run, and run in the mode you've scheduled your stream on. If the run loop isn't running, your stream won't do anything either.

JustSid
  • 25,168
  • 7
  • 79
  • 97
  • And how do I make sure it's actually run? The code above is from an NSOpeartion that is set as Concurrent and in the start method it starts main with detachNewThreadSelector .. I see no run loop code there... – user1028028 Aug 06 '14 at 08:00
  • If I replace mainRunLoop with currentRunLoop a runLoop Object is returned but nothing happens . Do I need to start it on my own? – user1028028 Aug 06 '14 at 11:02
  • @user1028028 Yes, that's what I was trying to tell you. Look into the documentation of `NSRunLoop`, and keep in mind that, by default, run loops run indefinitely. The question is, do you really benefit from a secondary thread? Scheduling the stream on the main run loop already makes it run non-blocking, so unless you also perform some heavy number crunching, the main run loop might be the best choice for you. – JustSid Aug 06 '14 at 11:19
  • @user1028028 Maybe it would be best to do the data fetching on the main run loop and the processing on a background dispatch queue or similar? – JustSid Aug 06 '14 at 11:49
  • @user1028028 This is really getting out of the scope of the initial question. Your best bet is to just stop in the debugger when the UI hangs and see what's going on at that time. But receiving data from an `NSStream` does not block the UI, even when scheduled on the main run loop. You probably also do the processing of the data on the main thread. – JustSid Aug 06 '14 at 12:24
  • @user1028028 Either your, the answer to your question is that the run loop you schedule the stream on isn't running. Instead of spinning up 30 `NSOperation`s which each drive their run loop concurrently, you probably want to spin a maximum of one thread up and schedule all streams on that one, and then schedule operations when necessary to process the data. More likely though, scheduling the streams on the main run loop is completely a ok for what you are doing and you won't need the extra thread. – JustSid Aug 06 '14 at 12:26
  • First, it's not good to schedule your stream on the main runloop - especially on standard mode. In that case, holding a menu open, or dragging event of a file on the desktop - will block the runloop mode temporarily and your stream will starve. – Motti Shneor Nov 11 '15 at 14:09
0

First, it's not good to schedule your stream on the main runloop - especially on standard mode.

In such setup case, holding a menu open, or dragging event of a file on the desktop, or any lengthy UI event handling actually, will block the runloop mode temporarily and starve your stream.

You should, indeed use a secondary thread for handling the stream. I'm trying now to do something similar to you - handle my stream from an NSOperation (which always runs in some unknown thread's context), but I found this discouraging paragraph in Apple's "Stream Programming Guide"

Before you open the stream to begin the streaming of data, send a scheduleInRunLoop:forMode: message to the stream object to schedule it to receive stream events on a run loop. By doing this, you are helping the delegate to avoid blocking when there is no data on the stream to read. If streaming is taking place on another thread, be sure to schedule the stream object on that thread’s run loop. You should never attempt to access a scheduled stream from a thread different than the one owning the stream’s run loop. Finally, send the NSInputStream instance an open message to start the streaming of data from the input source.

Now rescheduling the stream on the current operation's thread runloop, and running it... seems a huge overhead, and I'm not sure NSStream can handle rescheduling too frequently.

I don't know what is best to do about this, but I'm testing and studying, and will update as soon as I can.

You can, of course, create a special thread for the stream, and from your operation - synchronously call on that thread all access to the stream.

Motti Shneor
  • 2,095
  • 1
  • 18
  • 24