Threading NSStream - ios

I have a TCP connection that is open continuously for communication with an external device. There is a lot going on in the communication pipe which causes the UI to become unresponsive at times.
I would like to put the communication on a separate thread. I understand detachNewThread and how it calls a #selector. My issue is that I am not sure how this would be used in conjunction with something like NSStream?

Rather than manually creating a thread and managing thread safety issues, you might prefer to use Grand Central Dispatch ('GCD'). That allows you to post blocks — which are packets of code and some state — off to be executed away from the main thread and wherever the OS thinks is most appropriate. If you create a serial dispatch queue you can even be certain that if you post a new block while an old one has yet to finish, the system will wait until it finishes.
E.g.
// you'd want to make this an instance variable in a real program
dispatch_queue_t serialDispatchQueue =
dispatch_queue_create(
"A handy label for debugging",
DISPATCH_QUEUE_SERIAL);
...
dispatch_async(serialDispatchQueue,
^{
NSLog(#"all code in here occurs on the dispatch queue ...");
});
/* lots of other things here */
dispatch_async(serialDispatchQueue,
^{
NSLog(#"... and this won't happen until after everything already dispatched");
});
...
// cleanup, for when you're done
dispatch_release(serialDispatchQueue);
A very quick introduction to GCD is here, Apple's more thorough introduction is also worth reading.

Related

How can I "visualize"(observe) a thread exiting in iOS?

When learning thread and run loop, I notice that some articles say: "Generally, a thread just exits once it has done its work." So there is necessity sometimes to create a so-called "immortal thread"(?? I don't know the exact term in English) using NSRunloop.
The question is HOW can I prove the statement "just exits once it has done its work"? I code like this
- (void)doSomethingOnThread {
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// NSLog(#"I'm on thread %#", [NSThread currentThread]);
// });
NSThread *thread1 = [[NSThread alloc] initWithBlock:^{
NSLog(#"I'm on thread %#", [NSThread currentThread]);
}];
thread1.name = #"thread1";
[thread1 start];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(threadExitHandler:) name:NSThreadWillExitNotification object:nil];
}
- (void)threadExitHandler:(NSNotification *)noti {
NSLog(#"Thread will exit: %#", noti);
}
Well, the notification handler is not called.
So, [1]: How can I prove a thread exiting? [2]: What kinds of threads behave so?(I know the main thread will never exit, what about other thread? GCD threads, for example?)
If you want to visualize it, I might use the debugger. For example, I've set a breakpoint inside a NSThread subclass and I see the thread listed in the left panel in Xcode:
But if I have another breakpoint triggered one second after the main method finishes, I see the relevant “Thread will exit” message and my thread is no longer present :
Or you could add a NSLog statement inside the dealloc method for your NSThread subclass, and that also would demonstrate its deallocation. Or look for the subclass in the debug memory object graph.
Well, the notification handler is not called.
I'd suggest you add your observer for NSThreadWillExitNotification before you start your thread. Right now you have a race condition between the starting and exiting of this thread and the adding of the observer. FWIW, I do see the “Thread will exit” message.
Unrelated, while it’s great to learn about threads and runloops, it has little practical use nowadays. It might be more useful to master GCD which gets us out of the weeds of threads and offers performance optimizations and a richer API for writing robust multi-threaded code.
In regards to whether GCD creates persistent threads or not, the answer is yes, but we're abstracted away from this detail. But one of GCD’s performance optimizations is that it manages a “pool” of threads for us, not constantly spinning up new threads and destroying them constantly for every dispatched block of code.
You might want to watch WWDC 2016’s Concurrent Programming With GCD in Swift 3. It walks through the relationship between queues, threads, and runloops.

DispatchQueue bound to exact Thread

im developing an app, which uses some framework to draw 3D staff via openGL. This framework requires me to call draw() method from exact the same Thread.
So i created a serial DispatchQueue and started CADisplayLink in it, calling draw() at 60FPS. There are few other methods that i have to call from this exact thread, like start() and stop(). This makes queues perfect solution to me.
As you may know DispathQueue does not guaranteed to execute every task on the same thread. Which is quite stressful for me, as it may break my app.
I don't really like the idea to create NSThread and implement my own queue on it.
Are there any way to bind DispatchQueue to exact Thread? Maybe NSOperationQueue can be bound?
As Apple Documentation says:
When it comes to adding concurrency to an application, dispatch queues provide several advantages over threads. The most direct advantage is the simplicity of the work-queue programming model. With threads, you have to write code both for the work you want to perform and for the creation and management of the threads themselves. Dispatch queues let you focus on the work you actually want to perform without having to worry about the thread creation and management. Instead, the system handles all of the thread creation and management for you. The advantage is that the system is able to manage threads much more efficiently than any single application ever could. The system can scale the number of threads dynamically based on the available resources and current system conditions. In addition, the system is usually able to start running your task more quickly than you could if you created the thread yourself.
In simple words, you either work with dispatch queues, simply creating them and sending work to them, OR you work with NSThreads and NSRunLoops, creating them, setting them up, sending work to them, and possibly stopping them.
In detail:
NSThread / NSRunLoop
Creation:
self.thread = [[NSThread alloc] initWithTarget:self selector:#selector(threadMainRoutine) object:nil];
[self.thread start];
Start / management:
- (void)threadMainRoutine
{
// Set the runLoop variable, to signal this thread is alive
self.runLoop = [NSRunLoop currentRunLoop];
// Add a fake Mach port to the Run Loop, to avoid useless iterations of the main loop when the
// thread is just started (at this time there are no events added to the run loop, so it will
// exit immediately from its run() method)
[self.runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
//--- Thread main loop
while (thread_KeepRunning)
{
// Run the run loop. This function returns immediately if the RunLoop has nothing to do.
// NOTE: THIS STATEMENT:
// [self.runLoop run];
// DOES NOT WORK, although it is equivalent to CFRunLoopRun();
CFRunLoopRun();
}
// Unset the runLoop variable, to signal this thread is about to exit
self.runLoop = nil;
}
Adding work to be performed on it:
[self performSelector:#selector(mySelector:) onThread:myThread withObject:myObject waitUntilDone:YES];
Shutdown:
- (void)stop
{
if (self.thread) {
while (self.thread.isExecuting) {
thread_KeepRunning = NO;
CFRunLoopStop([self.runLoop getCFRunLoop]);
[NSThread sleepForTimeInterval:0.1f];
}
}
self.runLoop = nil;
self.thread = nil;
}
Dispatch Queue
Creation:
dispatch_queue_t myQueue = dispatch_queue_create("My Queue", DISPATCH_QUEUE_SERIAL);
Start:
dispatch_resume(myQueue);
Adding work to be performed on it:
dispatch_async(myQueue, (void)^ {
// put the work into this block
});
Shutdown:
dispatch_suspend(myQueue);
myQueue = nil;
In addition, Apple Documentation says that
Because Grand Central Dispatch manages the relationship between the tasks you provide and the threads on which those tasks run, you should generally avoid calling POSIX thread routines from your task code. If you do need to call them for some reason, you should be very careful about which routines you call
So: if you use dispatch queues, don't mess with threads.

Asynchronous NSStream I/O with GCD

I am working with an external device that I receive data from. I want to handle its data read/write queue asynchronously, in a thread.
I've got it mostly working: There is a class that simply manages the two streams, using the NSStreamDelegate to respond to incoming data, as well as responding to NSStreamEventHasSpaceAvailable for sending out data that's waiting in a buffer after having failed to be sent earlier.
This class, let's call it SerialIOStream, does not know about threads or GCD queues. Instead, its user, let's call it DeviceCommunicator, uses a GCD queue in which it initializes the SerialIOStream class (which in turn creates and opens the streams, scheduling them in the current runloop):
ioQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(ioQueue, ^{
ioStreams = [[SerialIOStream alloc] initWithPath:[#"/dev/tty.mydevice"]];
[[NSRunLoop currentRunLoop] run];
});
That way, the SerialIOStreams stream:handleEvent: method runs in that GCD queue, apparently.
However, this causes some problems. I believe I run into concurrency issues, up to getting crashes, mainly at the point of feeding pending data to the output stream. There's a critical part in the code where I pass the buffered output data to the stream, then see how much data was actually accepted into the stream, and then removing that part from my buffer:
NSInteger n = self.dataToWrite.length;
if (n > 0 && stream.hasSpaceAvailable) {
NSInteger bytesWritten = [stream write:self.dataToWrite.bytes maxLength:n];
if (bytesWritten > 0) {
[self.dataToWrite replaceBytesInRange:NSMakeRange(0, bytesWritten) withBytes:NULL length:0];
}
}
The above code can get called from two places:
From the user (DeviceCommunicator)
From the local stream:handleEvent: method, after being told that there's space in the output stream.
Those may be (well, surely are) running in separate thread, and therefore I need to make sure they do not run concurrently this code.
I thought I'd solve this by using the following code in DeviceCommunicator when sending new data out:
dispatch_async (ioQueue, ^{
[ioStreams writeData:data];
});
(writeData adds the data to dataToWrite, see above, and then runs the above code that sends it to the stream.)
However, that doesn't work, apparently because ioQueue is a concurrent queue, which may decide to use any available thread, and therefore lead to a race condition when writeData get called by the DeviceCommunicator while there's also a call to it from stream:handleEvent:, on separate threads.
So, I guess I am mixing expectations of threads (which I'm a bit more familiar with) into my apparent misunderstandings with GCD queues.
How do I solve this properly?
I could add an NSLock, protecting the writeData method with it, and I believe that would solve the issue in that place. But I am not so sure that that's how GCD is supposed to be used - I get the impression that'd be a cludge.
Shall I rather make a separate class, using its own serial queue, for accessing and modifying the dataToWrite buffer, perhaps?
I am still trying to grasp the patterns that are involved with this. Somehow, it looks like a classic producer / consumer pattern, but on two levels, and I'm not doing this right.
Long story, short: Don't cross the streams! (haha)
NSStream is a RunLoop-based abstraction (which is to say that it intends to do its work cooperatively on an NSRunLoop, an approach which pre-dates GCD). If you're primarily using GCD to support concurrency in the rest of your code, then NSStream is not an ideal choice for doing I/O. GCD provides its own API for managing I/O. See the section entitled "Managing Dispatch I/O" on this page.
If you want to continue to use NSStream, you can either do so by scheduling your NSStreams on the main thread RunLoop or you can start a dedicated background thread, schedule it on a RunLoop over there, and then marshal your data back and forth between that thread and your GCD queues. (...but don't do that; just bite the bullet and use dispatch_io.)

A proper way to wait for an event from a concurrent thread

I've got an occasional crash that has to do with the improperly finished task on a concurrent thread while an app is transitioning to background.
So I have 3 threads:
A (main).
B (managed by GCD).
C (manually created to process intensive socket operations).
The scenario is the following:
In the applicationDidEnterBackground: handler (which is certainly executed on thread A) a long-running task is begun on thread B to complete all ongoing operations (save an application state, close a socket, etc.). In this task I need to wait until a socket properly finishes its work on thread C and only after that to continue with this long-running task.
Below is simplified code:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Some synchronous task.
[stateManager saveState];
// Here I need to wait until the socket finishes its task.
...
// Continuing of the long-running task.
...
}
What is the acceptable way to accomplish this task. Is it OK if I do something like this?
while (socket.isConnected)
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
// Continuing of the long-running task.
Or maybe something wrong in my current architecture and I need to use NSOperation to serialize asynchronous tasks somehow for example?
update: The problem has been solved by using dispatch_semaphore APIs as #Rob Napier suggested.
First, do not think of these as threads, and if you're creating a thread with NSThread or performSelectorInBackground: (or even worse, pthreads), don't do that. Use GCD. GCD creates queues. Queues order blocks, which eventually do run on threads, but the threads are an implementation detail and there is not a 1:1 mapping of queues to threads. See Migrating Away from Threads for more discussion on that.
To the question of how to wait for some other operation, the tool you probably want is a dispatch_semaphore. You create the semaphore and hand it to both operations. Then you call dispatch_semaphore_wait when you want to wait for something, and dispatch_sempahore_signal when you want to indicate that that something has happened. See Using Dispatch Semaphores to Regulate the Use of Finite Resources for more example code. The "finite resource" in this case is "the socket." You want to wait until the other part is done using it and returned it to the pool.
Semaphores will work even if you are using manual threading, but I can't emphasize enough that you should not be doing manual threading. All your concurrency should be managed through GCD. This is an important tool to overall concurrency management in iOS.
I would use NSOperation with dependencies.
So, you have tasks
A - main thread - aka 'entry point'
B - heavy boy to run in background
C - something else heavy to run after socket finished
Your heavy task from "B" is OperationB
Assume your socket framework capable of running syncronous in current thread? - then this is your OperationSend
Do the rest to do in background - OperationC
there you have a chain of operations, dependent on each other:
OperationB -> OperationSend -> OperationC

How to open/create UIManagedDocument synchronously?

As mentioned in title, I would like to open UIManagedDocument synchronously, i.e, I would like my execution to wait till open completes. I'm opening document on mainThread only.
Current API to open uses block
[UIManagedDocument openWithCompletionHandler:(void (^)(BOOL success))];
Locks usage mentioned at link works well on threads other than main thread. If I use locks on mainThread, it freezes execution of app.
Any advice would be helpful. Thanks.
First, let me say that I strongly discourage doing this. Your main thread just waits, and does nothing while waiting for the call to complete. Under certain circumstances, the system will kill your app if it does not respond on the main thread. This is highly unusual.
I guess you should be the one to decide when/how you should use various programming tools.
This one does exactly what you want... block the main thread until the completion handler runs. Again, I do not recommend doing this, but hey, it's a tool, and I'll take the NRA stance: guns don't kill people...
__block BOOL waitingOnCompletionHandler = YES;
[object doSomethingWithCompletionHandler:^{
// Do your work in the completion handler block and when done...
waitingOnCompletionHandler = NO;
}];
while (waitingOnCompletionHandler) {
usleep(USEC_PER_SEC/10);
}
Another option is to execute the run loop. However, this isn't really synchronous, because the run loop will actually process other events. I've used this technique in some unit tests. It is similar to the above, but still allows other stuff to happen on the main thread (for example, the completion handler may invoke an operation on the main queue, which may not get executed in the previous method).
__block BOOL waitingOnCompletionHandler = YES;
[object doSomethingWithCompletionHandler:^{
// Do your work in the completion handler block and when done...
waitingOnCompletionHandler = NO;
}];
while (waitingOnCompletionHandler) {
NSDate *futureTime = [NSDate dateWithTimeIntervalSinceNow:0.1];
[[NSRunLoop currentRunLoop] runUntilDate:futureTime];
}
There are other methods as well, but these are simple, easy to understand, and stick out like a sore thumb so it's easy to know you are doing something unorthodox.
I should also note that I've never encountered a good reason to do this in anything other than tests. You can deadlock your code, and not returning from the main run loop is a slippery slope (even if you are manually executing it yourself - note that what called you is still waiting and running the loop again could re-enter that code, or cause some other issue).
Asynchronous APIs are GREAT. The condition variable approach or using barriers for concurrent queues are reasonable ways to synchronize when using other threads. Synchronizing the main thread is the opposite of what you should be doing.
Good luck... and make sure you register your guns, and always carry your concealed weapons permit. This is certainly the wild west. There's always a John Wesley Harden out there looking for a gun fight.

Resources