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;
Related
I've noticed a significant memory usage having the following function executed by a timer:
_timer = [NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(test)
userInfo:nil
repeats:YES];
- (void)test {
NSRunningApplication *app = [NSWorkspace sharedWorkspace].frontmostApplication;
app.processIdentifier;
}
The code basically does nothing.
Accessing almost any property (bundleIdentifier, bundleURL, description...) of a NSRunningApplication instance results into memory usage increasing at ~1MB/sec (considering the specified time interval). And the worst thing is that it never stops. I haven't tried to put it to the limit yet though...
I've tried to profile it using Instruments (Leaks template), but it finds no memory leaks.
Any clue?
Edit #1:
I've performed a simple experiment, creating a console application with a single swift file:
import Cocoa
while true {
guard let app = NSRunningApplication(processIdentifier: 315) else {
break
}
}
Put any pid you have running.
It takes a gig in a few seconds...
Edit #2:
My latest finding is that Process Type directly affects the behavior.
Consider:
TransformProcessType(&psn, UInt32(processType))
If processType = kProcessTransformToBackgroundApplication or kProcessTransformToUIElementApplication, I face the issue.
If process type = kProcessTransformToForegroundApplication (default value), everything works perfectly fine.
Long story short, I'm trying to create a simple console game that allows the user to input commands into the console in order to perform actions (such as start/stop) while at the same time printing out various properties and actions into the console.
_gameDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(_gameDispatchQueue, ^{
_propertyTimer=[NSTimer scheduledTimerWithTimeInterval: 0.1
target: mairne
selector: #selector(printProperties:)
userInfo: nil
repeats: YES];
[[NSRunLoop currentRunLoop] run];
});
And in my main.mm file I accept input via cin and then accept on it when they hit enter.
My issue with this code is that despite me trying to put it in a background thread, it still won't allow the user to input anything and hit enter. So for example if they want to write stop and hit enter, the command doesn't get processed.
How do I get NSTimer to run in the background printing things, but still allow user input to be processed?
Try running the timer on the main thread but inside your selector printProperties, have it run all of its contents in a dispatch queue like so:
// Init these somewhere on the main thread
_bgQueue = dispatch_queue_create("com.yourco.yourapp.bgQueue", NULL);
_propertyTimer=[NSTimer scheduledTimerWithTimeInterval: 0.1
target: mairne
selector: #selector(printProperties:)
userInfo: nil
repeats: YES];
- (void)printProperties:(id)sender
{
dispatch_async(_bgQueue, ^{
// do your work here
}
}
To expand somewhat on bbum's answer, NSFileHandle actually has a nice Objective-C API that wraps setting up a dispatch_source.
You can get an NSFileHandle for stdin like so:
NSFileHandle *standardInputHandle = [NSFileHandle fileHandleWithStandardInput];
NSFileHandle has a property called readabilityHandler that takes a block to be called (asynchronously) when data comes in. So, you can do something like this:
standardInputHandle.readabilityHandler = ^(NSFileHandle *fileHandle) { handleUserInputData([fileHandle availableData]); };
Assuming you can/want to handle incoming data line by line (ie. a return/newline is always the end of a command), this will probably work for you as is.
It's worth to try to use ncurses for such kind of console apps. getch with nodelay works as non-blocking key input.
void initialize_function() {
nodelay(stdscr, TRUE);
}
void function_will_be_called_from_NSRunLoop() {
int ch;
if ((ch = getch()) != ERR) {
/*
* user has pressed a key ch, pool it into a queue for parsing command
* like start, stop, or something like that
*/
}
/*
* print out various properties and actions into the ncurses screen (the console)
*/
}
ncurses 5.9
Ncurses Programming Guide
Move the reading from stdin off the main thread. Or, possibly, use a dispatch_source attached to the stdin file handle to provide a callback when data is available (buffering may be tricky).
Here's my scenario....
I have a Core MIDI app that detects Note On and Note Off messages which is working nicely.
I have have some midiSend methods that send messages back to the controller to illuminate LEDs - also working fine.
What I want to do now is on the Note Off message have the LED blink on and off. This is my code:
[midiListener performSelectorOnMainThread:#selector(startTimer:) withObject:midiMsgParts waitUntilDone:YES];
-(void)startTimer:(NSDictionary *)dict {
ledIntervalCount = 0;
ledIntervalTimer = [NSTimer scheduledTimerWithTimeInterval:0.3
target:self
selector:#selector(ledIntervalLoop:)
userInfo:dict
repeats:YES];
}
-(void)ledIntervalLoop:(NSTimer *)inboundTimer{
NSDictionary *userInfo = [NSDictionary dictionaryWithDictionary:[inboundTimer userInfo]];
NSLog(#"%#", userInfo);
UInt32 onCommand = [[userInfo objectForKey:#"noteOn"] intValue];
//UInt32 offCommand = [[userInfo objectForKey:#"noteOff"] intValue];
UInt32 theNote = [[userInfo objectForKey:#"note"] intValue];
ledIntervalCount++;
if (ledIntervalCount > 3) {
[ledIntervalTimer invalidate];
ledIntervalTimer = nil;
} else {
if(ledIntervalCount %2){
[self sendNoteOnIlluminate:onCommand midiNote:theNote];
}else{
[self sendNoteOnCommand:onCommand midiNote:theNote];
}
}
}
So I'm using an NSTimer to alternate the LED on/off commands. It works fine when I press a single button but not when I press multiple ones at the same time. It seems like it only picks on the last call to the startTimer method.
This is where I think I need to implement a dispatch queue with GCD. So that each NSTimer will execute in full without being interrupted by the method calls that follow.
Am I correct? Will GCD allow me to have the NSTimer run concurrently?
GCD is a new concept to me so some guidance on how I might implement it would help. I've read through some of the reference guides but need to see some example code in the context of my scenario. I guess what I'm asking here is, what part of my code would go in the block?
AH you invalidate the timers anyway... after 3 tries. ALL -- you need X counters for X timers, you have 1 counter for X timer
instead of one long ledIntervalCount, have a NSMutableArray with ledIntervalCounts! One per timer
then in the userInfo for the timer, provide the index to the counter that is to be used
The problem was that I was calling the class from within a method wrapped in an autorelease. I now run this on the main thread and it works fine.
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.
I have an app that successfully uses the synchronous methods to download files (NSData's initWithContentsOfURL and NSURLConnection's sendSynchronousRequest), but now I need to support large files. This means I need to stream to disk bit by bit. Even though streaming to disk and becoming asynchronous should be completely orthoganal concepts, Apple's API forces me to go asynchronous in order to stream.
To be clear, I am tasked with allowing larger file downloads, not with re-architecting the whole app to be more asynchronous-friendly. I don't have the resources. But I acknowledge that the approaches that depend on re-architecting are valid and good.
So, if I do this:
NSURLConnection* connection = [ [ NSURLConnection alloc ] initWithRequest: request delegate: self startImmediately: YES ];
.. I eventually have didReceiveResponse and didReceiveData called on myself. Excellent. But, if I try to do this:
NSURLConnection* connection = [ [ NSURLConnection alloc ] initWithRequest: request delegate: self startImmediately: YES ];
while( !self.downloadComplete )
[ NSThread sleepForTimeInterval: .25 ];
... didReceiveResponse and didReceiveData are never called. And I've figured out why. Weirdly, the asynchronous download happens in the same main thread that I'm using. So when I sleep the main thread, I'm also sleeping the thing doing the work. Anyway, I have tried several different ways to achieve what I want here, including telling the NSURLConnection to use a different NSOperationQueue, and even doing dispatch_async to create the connection and start it manually (I don't see how this couldn't work - I must not have done it right), but nothing seems to work. Edit: What I wasn't doing right was understanding how Run Loops work, and that you need to run them manually in secondary threads.
What is the best way to wait until the file is done downloading?
Edit 3, working code:
The following code actually works, but let me know if there's a better way.
Code executing in the original thread that sets up the connection and waits for the download to complete:
dispatch_queue_t downloadQueue = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 );
dispatch_async(downloadQueue, ^{
self.connection = [ [ NSURLConnection alloc ] initWithRequest: request delegate: self startImmediately: YES ];
[ [ NSRunLoop currentRunLoop ] run ];
});
while( !self.downloadComplete )
[ NSThread sleepForTimeInterval: .25 ];
Code executing in the new thread that responds to connection events:
-(void)connection:(NSURLConnection*) connection didReceiveData:(NSData *)data {
NSUInteger remainingBytes = [ data length ];
while( remainingBytes > 0 ) {
NSUInteger bytesWritten = [ self.fileWritingStream write: [ data bytes ] maxLength: remainingBytes ];
if( bytesWritten == -1 /*error*/ ) {
self.downloadComplete = YES;
self.successful = NO;
NSLog( #"Stream error: %#", self.fileWritingStream.streamError );
[ connection cancel ];
return;
}
remainingBytes -= bytesWritten;
}
}
-(void)connection:(NSURLConnection*) connection didFailWithError:(NSError *)error {
self.downloadComplete = YES;
[ self.fileWritingStream close ];
self.successful = NO;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
self.downloadComplete = YES;
[ self.fileWritingStream close ];
self.successful = YES;
}
... didReceiveResponse and didReceiveData are never called. And I've
figured out why. Weirdly, the asynchronous download happens in the
same main thread that I'm using. It doesn't create a new thread. So
when I sleep the main thread, I'm also sleeping the thing doing the
work.
Exactly. The connection is driven by the run loop; if you sleep the thread, the run loop stops, and that prevents your connection from doing its thing.
So don't do anything special. Let the app sit there, with the run loop running. Maybe put a little spinner on the screen to entertain the user. Go about your business if you can. If at all possible, let the user continue to use the application. Your delegate method will be called when the connection is complete, and then you can do what you need to do with the data.
When you move your code to a background thread, you'll again need a run loop to drive the connection. So you'll start create a run loop, schedule your connection, and then just return. The run loop will keep running, and your delegate method will again be called when the connection completes. If the thread is done, you can then stop the run loop and let the thread exit. That's all there is to it.
Example: Let's put this in concrete terms. Let's say that you want to make a number of connections, one at a time. Stick the URL's in a mutable array. Create a method called (for example) startNextConnection that does the following things:
grabs an URL from the array (removing it in the process)
creates an URL request
starts a NSURLConnection
return
Also, implement the necessary NSURLConnectionDelegate methods, notably connectionDidFinishLoading:. Have that method do the following:
stash the data somewhere (write it to a file, hand it to another thread for parsing, whatever)
call startNextConnection
return
If errors never happened, that'd be enough to retrieve the data for all the URLs in your list. (Of course, you'll want startNextConnection to be smart enough to just return when the list is empty.) But errors do happen, so you'll have to think about how to deal with them. If a connection fails, do you want to stop the entire process? If so, just have your connection:didFailWithError: method do something appropriate, but don't have it call startNextConnection. Do you want to skip to the next URL on the list if there's an error? Then have ...didFailWithError: call startNextRequest.
Alternative: If you really want to keep the sequential structure of your synchronous code, so that you've got something like:
[self downloadURLs];
[self waitForDownloadsToFinish];
[self processData];
...
then you'll have to do the downloading in a different thread so that you're free to block the current thread. If that's what you want, then set up the download thread with a run loop. Next, create the connection using -initWithRequest:delegate:startImmediately: as you've been doing, but pass NO in the last parameter. Use -scheduleInRunLoop:forMode: to add the connection to the download thread's run loop, and then start the connection with the -start method. This leaves you free to sleep the current thread. Have the connection delegate's completion routine set a flag such as the self.downloadComplete flag in your example.
I hesitate to provide this answer because the others are correct that you really should structure your app around the asynchronous model. Nevertheless:
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
NSString* myPrivateMode = #"com.yourcompany.yourapp.DownloadMode";
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:myPrivateMode];
[connection start];
while (!self.downloadComplete)
[[NSRunLoop currentRunLoop] runMode:myPrivateMode beforeDate:[NSDate distantFuture]];
Do not do this on the main thread. Your app is just as likely to be terminated for blocking the main thread as for downloading too big a file to memory.
By the way, given that you're downloading to a file instead of memory, you should consider switching from NSURLConnection to NSURLDownload.
I think your sleepForInterval is blocking the NSURLConnection's activity -
No run loop processing occurs while the thread is blocked.
From the NSThread documentation.
I think you might have to rethink how you're setting your downloadComplete variable. Consider using your connectionDidFinishLoading:connection delegate method to determine when the download is complete instead of your loop + sleep?
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
self.downloadComplete = YES;
// release the connection, and the data object
[connection release];
[receivedData release];
}
From the NSURLConnection guide.
You can use the connection:connection didFailWithError:error delegate method to ensure you're dealing with situations where the download does not complete.