High CPU consumption and latency while reading serial data - objective-c

I have two functions in my software that cause important latency problems. The software is written in Objective-C. I receive serial data from an usb device and my goal is to encapsulate them and then to send them to another object which will process the data.
This section of the program causes large cpu and latency issues and I don't know how to solve this at all. The device only sends data when its state changes, thus when lots of changes occur well everything becomes laggy..
- (void)getSerialData {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[self getSerialDataLoop];
});
}
- (void)getSerialDataLoop {
readThreadRunning = YES;
char byte_buffer[2]; // buffer for holding incoming data
int numBytes=0; // number of bytes read during read
NSString *text;
// this will loop untilthe serial port closes
while(TRUE) {
// read() blocks until some data is available or the port is closed
numBytes = (int)read(serialFileDescriptor, byte_buffer, 1); // read up to the size of the buffer
if(numBytes>0) {
///text = [NSString stringWithCString:byte_buffer encoding:NSSymbolStringEncoding];
if(![text isEqualToString:#""]){
text = [NSString stringWithUTF8String:byte_buffer];
[self performSelectorOnMainThread:#selector(processNSStringData:) withObject:text waitUntilDone:YES];
}
} else {
break; // Stop the thread if there is an error
}
}
// make sure the serial port is closed
if (serialFileDescriptor != -1) {
close(serialFileDescriptor);
serialFileDescriptor = -1;
}
// mark that the thread has quit
readThreadRunning = FALSE;
}
Do you have any ideas or pointers?

You've basically reinvented NSStream here. I would first recommend that you investigate this already available solution that ties into the run loop.
You also could easily be overwhelming yourself with calls to getSerialData. Nothing in your system prevents multiple calls to this routine, and if you make multiple calls, you'll get dueling concurrent operations. Using NSStream would address that. In any case, though, you shouldn't keep creating new read blocks if one is already running.
You're also reading one byte at time and processing it. This is likely your biggest impact. Calling back to the main thread for every byte is likely quite expensive. If nothing else you're creating a new NSString object for every byte.
Note that your code is very dangerous and could crash. You never initialize byte_buffer, and you only read one byte into it. When you call stringWithUTF8String:, you're assuming that the second byte is \0, which depends on the current state of the stack.

Related

Mac equivalent to .Net SerialPort.ReadExisting()

I'm working on porting a VB program to the PC. It uses serial communication to interact with a physical device. I have a version up and running on the Mac using ORSSerialPort. However, once piece of the VB library that is great is the SerialPort.ReadExisting() function. This essentially reads any messages and discards them.
Has anyone built something similar on the Mac side? I've tried pulling out the ORSSerialPort into a function to directly read values (see below). However, unless I send a message I receive a null response. The readExisting function would be great for a scenario when things get a little out of alignment such as:
I send a message "Message1" to the device and nothing happens (was expecting Response1).
I send a message "Message2" to the device and receive: "Response1" instead of "Response2"
I'd like to detect this, call an equivalent to SerialPort.readExisting() since Response2 is the next to be found if I continue.
My read function:
-(NSString *) directRead
{
// Read Directly
int localPortFD = self.fileDescriptor;
struct timeval timeout;
int result=0;
fd_set localReadFDSet;
FD_ZERO(&localReadFDSet);
FD_SET(localPortFD, &localReadFDSet);
timeout.tv_sec = 0;
timeout.tv_usec = 100000; // Check to see if port closed every 100ms
result = select(localPortFD+1, &localReadFDSet, NULL, NULL, &timeout);
if (!self.isOpen) return nil; // Port closed while select call was waiting
if (result < 0)
{
NSLog(#"No Data To Read");
}
if (result == 0 || !FD_ISSET(localPortFD, &localReadFDSet)) return nil;
// Data is available
char buf[1024];
long lengthRead = read(localPortFD, buf, sizeof(buf));
if (lengthRead>0)
{
NSData *readData = [NSData dataWithBytes:buf length:lengthRead];
if (readData != nil)
return [[NSString alloc] initWithData:readData encoding:NSUTF8StringEncoding];
}
return nil;
}
You would think just doing:
NSString *result = nil;
do
{
result = [serialPort directRead];
NSLog(#"Past Message is: %#", result);
} while(result != nil);
Would flush out the messages. However, it acts as if there aren't any messages. However, if I call sendData:Message1 again I'd still see Response2 show up (in the above scenario).
Thanks for any and all help.
I'm having a little trouble following exactly what you're trying to do here, but in any case, it's something you'll have to implement at a higher level than ORSSerialPort or the POSIX serial read functions. ORSSerialPort will tell you any time bytes are available, as reported by the underlying standard POSIX file APIs it uses. So, there's nothing to "flush" from its perspective. If you get a response you were not expecting, e.g. a response to an "old" request, there's no way at all for the serial port to know that, it's up to your code to figure it out.
Something like this:
- (void)serialPort:(ORSSerialPort *)serialPort didReceiveData:(NSData *)data
{
if (![self dataIsResponseToLatestRequest:data]) return; // Try again next time around
// Process a valid (expected response)
}
This raises a couple other issues. You can't be guaranteed that data will come in "whole packets". The serial hardware, low level serial APIs, and ORSSerialPort have no way of knowing what a whole packet looks like, and therefore just deliver data a bit at a time as it arrives. See this answer for more on that. So, it's up to you to buffer incoming data and assemble it into packets.
If you need to match requests you sent with responses coming in, you have to do that yourself. (sounds like you're really already doing this).
Lastly, if you're only receiving "Response1" after sending "Message2", this leads me to think there's a problem with the device on the other side of the connection not responding to requests properly. It would probably be worth fixing that, if you can (assuming it's your hardware/software).

malloc causing heap corruption

I'm learning how to communicate with USB devices with IOKit, and I wrote this piece of code:
// Global variable
char *dataBuffer;
- (void)startPolling {
if (!shouldPoll) { // Prevent polling twice
shouldPoll = YES;
timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:#selector(poll) userInfo:nil repeats:NO];
[self performSelectorInBackground:#selector(poll) withObject:nil];
}
}
- (void)poll {
dataBuffer = (char *)malloc(numBytes);
numBytes = 64;
returnCode = (*usbInterface)->ReadPipe(usbInterface, 2, dataBuffer, &numBytes);
// Handle received data in dataBuffer
free(dataBuffer);
[timer fire];
}
It works like this: another part of the code that works fine looks for the device, opens it, and then opens the correct interface. After that, when the user presses a button, it will call startPolling, which will set a timer that triggers the method poll every 0.5 seconds (sorta, the timer will fire again repeatedly).
In the poll method, the program will read the USB pipe and store data on dataBuffer. At first, I thought that I could alloc its memory once and reuse the pointer at every iteration, but for reasons I'm unfamiliar, the second ReadPipe call would fail. Always.
In an act of desperation, I came up with this (terrible?) idea: allocate and free the buffer memory at every iteration. For my surprise it did work, and I was able to read the device successfully.
The problem is that from time to time the program crashes with the error:
malloc: *** error for object 0x610000005890: Heap corruption detected, free list canary is damaged
*** set a breakpoint in malloc_error_break to debug
I really don't know what that means, let alone how to solve it. I set the buffer size to 64 to make sure that any data read would fit in the memory. Actual data is 18 bytes long.
Any clues?
These two statements should be the other way around:
dataBuffer = (char *)malloc(numBytes);
numBytes = 64;

How to implement smooth GCD I/O

Having a rough go here with scheduling disk reads via GCD.
Below is a code snippet to load frames from a file that contains about frameCount=1000 frames. In my initial implementation, I did this from the main thread:
[self readFramesFromFrame:0 toFrame:frameCount];
And here's my method:
-(BOOL)readFramesFromFrame:(NSInteger)startFrame toFrame:(NSInteger)endFrame
{
if (frameCount<=0)
return YES;
__block BOOL endRead;
dispatch_async(diskQueue, ^{
do {
dispatch_async(frameQueue, ^{
for (NSInteger i=startFrame; i<endFrame; i++)
[self readFileFrame:i];
});
// ** BEGIN get next batch
NSInteger newStart = endFrame;
NSInteger newEnd = ((endFrame+highWater) < frameCount) ? endFrame+highWater : frameCount;
if (newStart==frameCount)
endRead=YES;
else
endRead=[self readFramesFromFrame:(NSInteger)newStart toFrame:(NSInteger)newEnd];
// ** END get next batch
} while (!endRead);
});
return YES;
}
However, I don't want to load up the initial run with 1000 frames as it takes too long.
I initially want to only load 20 frames (my highwater amount), so I rejigged the code and made an revised call of:
[self readFramesFromFrame:0 toFrame:(frameCount<highWater) ? frameCount : highWater];
But this still takes too long before I get access to the first frame for processing. I am trying to schedule separate blocks of work rather than one large block of work, but I realize I am still effectively scheduling all frames. No improvement.
Two points of explanation. Firstly, my call to [self readFileFrame:frameNumber] does a dispatch_io_read using a readQueue, that is throttled elsewhere by my downstream processing handlers by calling either dispatch_suspend(readQueue) or dispatch_resume(readQueue). I use a low-water value of 10 frames and a high-water value of 20 frames which suspend/resume the readQueue as appropriate. That is working swimmingly well, but is currently predicated upon a reasonably stuff queue of frames.
Secondly, my call to readFileFrame will produce a valid frame of data via the readQueue thread that is accessed (and displayed) via a separate GCD timer.
I have tried dispatching the code snippet between the comments on "next batch" back on the main queue, but that is a disaster also. I thought that adding an additional wrapping serial private queue frameQueue would help, but no.
If I pretend that frameCount is, say, 50 frames, things work very quickly and swimmingly well -- but I only get 50 frames and no more.
How do I rejig this code snippet so that it lazily reads in batches of frames?

Data gets truncated when sending over NSStream

Ok so I have been at this bug all day, and I think I've got it narrowed down to the fundamental problem.
Background:
I am working on an app that has required me to write my own versions of NSNetService and NSNetServiceBrowser to allow for Bonjour over Bluetooth in iOS 5. It has been a great adventure, as I knew nothing of network programming before I started this project. I have learned a lot from various example projects and from the classic Unix Network Programming textbook. My implementation is based largely on Apple's DNSSDObjects sample project. I have added code to actually make the connection between devices once a service has been resolved. An NSInputStream and an NSOutputStream are attained with CFStreamCreatePairWithSocketToHost( ... ).
Problem:
I am trying to send some data over this connection. The data consists of an integer, a few NSStrings and an NSData object archived with NSKeyedArchiver. The size of the NSData is around 150kb so the size of the whole message is around 160kb. After sending the data over the connection I am getting the following exception when I try to unarchive...
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '*** -[NSKeyedUnarchiver initForReadingWithData:]: incomprehensible archive
After further exploration I have noticed that the received data is only about 2kb.. The message is being truncated, thus rendering the archive "incomprehensible."
Potentially relevant code:
The method that sends the data to all connected devices
- (void) sendMessageToPeers:(MyMessage *)msg
{
NSEnumerator *e = [self.peers objectEnumerator];
//MyMessage conforms to NSCoding, messageAsData getter calls encodeWithCoder:
NSData *data = msg.messageAsData;
Peer *peer;
while (peer = [e nextObject]) {
if (![peer sendData:data]) {
NSLog(#"Could not send data to peer..");
}
}
}
The method in the Peer class that actually writes data to the NSOutputStream
- (BOOL) sendData:(NSData *)data
{
if (self.outputStream.hasSpaceAvailable) {
[self.outputStream write:data.bytes maxLength:data.length];
return YES;
}
else {
NSLog(#"PEER DIDN'T HAVE SPACE!!!");
return NO;
}
}
NSStreamDelegate method for handling stream events ("receiving" the data)
The buffer size in this code is 32768 b/c that's what was in whatever example code I learned from.. Is it arbitrary? I tried changing it to 200000, thinking that the problem was just that the buffer was too small, but it didn't change anything.. I don't think I fully understand what's happening.
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case NSStreamEventHasBytesAvailable: {
NSInteger bytesRead;
uint8_t buffer[32768]; // is this the issue?
// uint8_t buffer[200000]; //this didn't change anything
bytesRead = [self.inputStream read:buffer maxLength:sizeof(buffer)];
if (bytesRead == -1) ...;
else if (bytesRead == 0) ...;
else {
NSData *data = [NSData dataWithBytes:buffer length:bytesRead];
[self didReceiveData:data];
}
} break;
/*omitted code for other events*/
}
}
NSStream over a network like that will be using a TCP connection. It can vary, but the maximum packet size is often around 2k. As the message you’re sending is actually 160k, it will be split up into multiple packets.
TCP abstracts this away to just be a stream of data, so you can be sure all these packets will receive in the correct order.
However, the stream:handleEvent: delegate method is probably being called when only the first 2k of data has arrived – there’s no way for it to know that there’s more coming until it does.
Note the method read:maxLength: doesn’t gauruntee you’ll always get that max length – in this case it seems to be only giving you up to 2k.
You should count up the actual bytesReceived, and concatenate all the data together until you receive the total amount you’re waiting for.
How does the receiver know how much data it wants? – you might want to design your protocol so before sending data, you send an integer of defined size indicating the length of the coming data. Alternatively, if you’re only ever sending one message over the socket, you could simply close it when finished, and have the receiver only unarchive after the socket is closed.
You seem to be reading from self.inputStream but the stream passed into your stream:handleEvent: method is called aStream. Are they referencing the same object somehow? Otherwise I'm not sure you're reading the stream that actually has bytes available

Locked up waiting for #synchronized

I have this (rare) odd case where my objective-c iOS program is locking up. When I break into the debugger, there are two threads and both of them are stuck at a #synchronized().
Unless I am completely misunderstanding #synchronized, I didn't think that was possible and the whole point of the command.
I have a main thread and worker thread that both need access to a sqlite database, so I wrap the chunks of code that are accessing the db in #synchronized(myDatabase) blocks. Not much else happens in these blocks except the db access.
I'm also using the FMDatabase framework to access sqlite, I don't know if that matters.
The myDatabase is a global variable that contains the FMDatabase object. It is created once at the start of the program.
I know I'm late to the party with this, but I've found a strange combination of circumstances that #synchronized handles poorly and is probably responsible for your problem. I don't have a solution to it, besides to change the code to eliminate the cause once you know what it is.
I will be using this code below to demonstrate.
- (int)getNumberEight {
#synchronized(_lockObject) {
// Point A
return 8;
}
}
- (void)printEight {
#synchronized(_lockObject) {
// Point B
NSLog(#"%d", [self getNumberEight]);
}
}
- (void)printSomethingElse {
#synchronized(_lockObject) {
// Point C
NSLog(#"Something Else.");
}
}
Generally, #synchronized is a recursively-safe lock. As such, calling [self printEight] is ok and will not cause deadlocks. What I've found is an exception to that rule. The following series of events will cause deadlock and is extremely difficult to track down.
Thread 1 enters -printEight and acquires the lock.
Thread 2 enters -printSomethingElse and attempts to acquire the lock. The lock is held by Thread 1, so it is enqueued to wait until the lock is available and blocks.
Thread 1 enter -getNumberEight and attempts to acquire the lock. The lock is held already and someone else is in the queue to get it next, so Thread 1 blocks. Deadlock.
It appears that this functionality is an unintended consequence of the desire to bound starvation when using #synchronized. The lock is only recursively safe when no other thread is waiting for it.
The next time you hit deadlock in your code, examine the call stacks on each thread to see if either of the deadlocked threads already holds the lock. In the sample code above, by adding long sleeps at Point A, B, and C, the deadlock can be recreated with almost 100% consistency.
EDIT:
I'm no longer able to demonstrate the previous issue, but there is a related situation that still causes issues. It has to do with the dynamic behavior of dispatch_sync.
In this code, there are two attempts to acquire the lock recursively. The first calls from the main queue into a background queue. The second calls from the background queue into the main queue.
What causes the difference in behavior is the distinction between dispatch queues and threads. The first example calls onto a different queue, but never changes threads, so the recursive mutex is acquired. The second changes threads when it changes queues, so the recursive mutex cannot be acquired.
To emphasize, this functionality is by design, but it behavior may be unexpected to some that do not understand GCD as well as they could.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSObject *lock = [[NSObject alloc] init];
NSTimeInterval delay = 5;
NSLog(#"Example 1:");
dispatch_async(queue, ^{
NSLog(#"Starting %d seconds of runloop for example 1.", (int)delay);
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:delay]];
NSLog(#"Finished executing runloop for example 1.");
});
NSLog(#"Acquiring initial Lock.");
#synchronized(lock) {
NSLog(#"Acquiring recursive Lock.");
dispatch_sync(queue, ^{
NSLog(#"Deadlock?");
#synchronized(lock) {
NSLog(#"No Deadlock!");
}
});
}
NSLog(#"\n\nSleeping to clean up.\n\n");
sleep(delay);
NSLog(#"Example 2:");
dispatch_async(queue, ^{
NSLog(#"Acquiring initial Lock.");
#synchronized(lock) {
NSLog(#"Acquiring recursive Lock.");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"Deadlock?");
#synchronized(lock) {
NSLog(#"Deadlock!");
}
});
}
});
NSLog(#"Starting %d seconds of runloop for example 2.", (int)delay);
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:delay]];
NSLog(#"Finished executing runloop for example 2.");
I stumbled into this recently, assuming that #synchronized(_dataLock) does what it's supposed to do, since it is such a fundamental thing after all.
I went on investigating the _dataLock object, in my design I have several Database objects that will do their locking independently so I was simply creating _dataLock = [[NSNumber numberWithInt:1] retain] for each instance of Database.
However the [NSNumber numberWithInt:1] returns the same object, as in same pointer!!!
Which means what I thought was a localized lock for only one instance of Database is not a global lock for all instances of Database.
Of course this was never the intended design and I am sure this was the cause of issues.
I will change the
_dataLock = [[NSNumber numberWithInt:1] retain]
with
_dataLock = [[NSUUID UUID] UUIDString] retain]