monitoring ifstream read progress from separate thread in Obj-C - objective-c

This is the code I'm using to write and read a file in the background using GCD.
#import "AppDelegate.h"
#import <dispatch/dispatch.h>
#import <iostream>
#import <fstream>
size_t fileSize = 1024 * 1024 * 10;
std::ofstream *osPtr = 0;
std::ifstream *isPtr = 0;
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
const float framerate = 40;
const float frequency = 1.0f/framerate;
[NSTimer scheduledTimerWithTimeInterval:frequency
target:self selector:#selector(doSomething)
userInfo:nil repeats:YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
std::ofstream os("myFile", std::ios::binary);
if (os) {
osPtr = &os;
for (int i = 0; i<fileSize; i++) {
os << 'c';
}
osPtr = 0;
os.close();
printf("write done\n");
}
std::ifstream is("myFile", std::ios::binary);
if (is) {
is.seekg(0, std::ifstream::end);
fileSize = (size_t)is.tellg();
is.seekg(0, std::ifstream::beg);
isPtr = &is;
while ( is.good() )
{
char c;
is >> c;
}
isPtr = 0;
is.close();
printf("read done\n");
}
});
}
- (void)doSomething
{
// write file progress indicator
if (osPtr)
printf("%5.1f\n", (float)osPtr->tellp()/fileSize*100.0f);
// read file progress indicator
if (isPtr)
printf("%5.1f\n", (float)isPtr->tellg()/fileSize*100.0f);
}
#end
It writes ok, but when reading big files (5 mb or more) an EXEC_BAD_ACCESS error is thrown, within the streambuf class code.
template <class _CharT, class _Traits>
inline _LIBCPP_INLINE_VISIBILITY
typename basic_streambuf<_CharT, _Traits>::int_type
basic_streambuf<_CharT, _Traits>::sbumpc()
{
if (__ninp_ == __einp_)
return uflow();
return traits_type::to_int_type(*__ninp_++); //<---EXEC_BAD_ACCESS
}
This is the project test.zip

Does the documentation of std::of stream say that it is thread safe? I don't think so.
My bet would be that you always get a crash if your progress function is called while the osPtr or isPtr exists. But for small files, the writing/reading is so fast that they are both gone before your progress method is ever called.

The best way to read and write files asynchronously would be to use the GCD IO functions...
There is a convenience read function (and a similar write function).
void dispatch_read(
dispatch_fd_t fd,
size_t length,
dispatch_queue_t queue,
void (^handler)(dispatch_data_t data, int error));
Your handler block would be called back each time the system had some data ready to be read, and you could update your progress indicator.
You could use dispatch_after with the same queue, and they would be automatically seriallized (as long as you used a serial queue).
However, just to be clear: your problem is that you are accessing the stream objects from multiple threads at the same time. One thread is running the queue code block, and another is running your timer call. They are both trying to access the same stream objects. Bad news.
If you want to continue to use your method of IO, you need to serialize access in one of several ways. You can create a class that provides safe access to an IOStream across multiple threads, or you can serialize the access yourself with locks. Both C++ and Obj-C provide many synchronization APIs.
However, there is a very common idiom used in lots of apple code: the delegate.
In your case, a simple progress delegate, with a method that sends the current progress, would suffice. This way the delegate is called from within the context of the long running task, which means you have synchronized access to any shared data.
If you want, you can dispatch any GUI work to the main thread with GCD.

So this would be my approach.
I need to subclass std::streambuf, even when it can be overkill (like it is posted here or here), at least for a feature that should be quite common in multithreaded applications.

Related

NSThread waking up

I would like to know how I can achieve the following in objective-c,
I am doing communication with FTDI232R modem using serial communication,So I am using POSIX call to open,write and read from the path of the modem(dev/tty/nameOfModem).
The POSIX calls are synchronous calls so while read I don't want to block my main thread hence I am thinking to do read call in separate thread.
I don't want this secondary thread to run continuously but wake up only when there is something to read and after read is completed it should sleep.I went through the documentation and read about providing an input source to the NSRunLoop and adding that runloop to the secondary thread,but couldn't figure out how to do it.
Thank you in advance for all your help.
You typically have a BOOL to indicate your run state, and then a date to run until. When doing socket-y things, I tend to do something like:
NSDate *beforeDate = [NSDate dateWithTimeIntervalSinceNow:.1];
while (self.isActive && [[NSRunLoop currentRunLoop] runMode: NSRunLoopCommonModes beforeDate:beforeDate]) {
beforeDate = [NSDate dateWithTimeIntervalSinceNow:.1];
}
And then when you disconnect from your modem, you can set isActive to NO to let the runloop spin down.
While not exactly what you want, Apple's docs on threading with NSOperation might be interesting for you to skim.
You should probably use GCD dispatch sources for this. Here's example code copied directly out of that article:
dispatch_source_t ProcessContentsOfFile(const char* filename)
{
// Prepare the file for reading.
int fd = open(filename, O_RDONLY);
if (fd == -1)
return NULL;
fcntl(fd, F_SETFL, O_NONBLOCK); // Avoid blocking the read operation
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
fd, 0, queue);
if (!readSource)
{
close(fd);
return NULL;
}
// Install the event handler
dispatch_source_set_event_handler(readSource, ^{
size_t estimated = dispatch_source_get_data(readSource) + 1;
// Read the data into a text buffer.
char* buffer = (char*)malloc(estimated);
if (buffer)
{
ssize_t actual = read(fd, buffer, (estimated));
Boolean done = MyProcessFileData(buffer, actual); // Process the data.
// Release the buffer when done.
free(buffer);
// If there is no more data, cancel the source.
if (done)
dispatch_source_cancel(readSource);
}
});
// Install the cancellation handler
dispatch_source_set_cancel_handler(readSource, ^{close(fd);});
// Start reading the file.
dispatch_resume(readSource);
return readSource;
}

Objective-C Cocoa how to correctly use run loop in GCD

I'm not sure how to correctly use GCD in a run loop situation where the thread might need to be stopped. The problem starts from the outset, and how or where to use CGEventCallback (which is absent from my code). The stop button won't stop the loop, and I don't think my dispatch queue is setup properly -- along with the while loop creating a huge lag.
I've read top question-answers from the search, like this and this, but one is for iOS and the other isn't relevant. Could someone show me how to properly do this?
my code:
// .h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSTextField *textFieldBox;
IBOutlet NSButton *stop;
}
#property (assign) IBOutlet NSWindow *window;
- (void)stop;
#end
// .m
#import "AppDelegate.h"
#implementation AppDelegate
BOOL isActive = FALSE;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self mainMethod];
}
- (void)mainMethod {
NSLog(#"loop started");
isActive = TRUE;
[self theArbitraryNonCompliantLoop];
NSLog(#"doing other stuff");
}
- (void)stop {
isActive = FALSE;
return;
}
- (void)theArbitraryNonCompliantLoop {
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
while (isActive) {
for (NSUInteger i = 0; i < 1000000; i++) {
[textFieldBox setStringValue:[NSString stringWithFormat:#"%lu",(unsigned long)i]];
}
}
});
}
#end
Ignoring the name, the for loop needs to test isActive as well. That will solve the latency issue.
The UI update needs to be done on the main thread which is easy because you can just schedule a block on the main queue to do it.
- (void)theArbitraryNonCompliantLoop {
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
while (isActive)
{
for (NSUInteger i = 0; isActive && i < 1000000; i++)
{
dispatch_async(dispatch_get_main_queue(),
^{ [textFieldBox setStringValue:[NSString stringWithFormat:#"%lu",(unsigned long)i]] };
}
}
});
}
There are still some issues here. I think, as it stands it will flood the main thread's run loop with events, so some throttling will be required. You might also consider some synchronisation for the inActive instance variable in case the compiler optimises it by pulling it into a register at the beginning of the method. Also, it will be subject to race conditions thanks to caching etc.
Big mistake: You are changing a UI element on a background thread. That will cause all kinds of problems. Don't do that.
You seem to be quite confused what a runloop is. You are also trying to confuse people by calling something "theRunLoop" that just does stuff on a background thread. Your code has nothing to do with the runloop, and until you understand what a runloop is, better keep away from it.
Why would you call an arbitrary method theRunLoop?
Either way, quoting Run Loops (Threading Programming Guide):
Both Cocoa and Core Foundation provide run loop objects to help you
configure and manage your thread’s run loop. Your application does not
need to create these objects explicitly; each thread, including the
application’s main thread, has an associated run loop object. Only
secondary threads need to run their run loop explicitly, however. The
app frameworks automatically set up and run the run loop on the main
thread as part of the application startup process.
My guess would be that your while loop is still on its first run. The 1000000 for loop is probably taking too long which is why it still seems like the loop is still running. To test it out put an NSLog after your for loop to see if it has exited it after you changed isActive to false.

Objective-C Threading

I wanted to learn more about threading in objective-c, so I made a little test program that would just loop and output what iteration of the loop it is on.
However, the output I am getting is not what I am expecting. I have an idea as to why, but first here is my code:
main.m
#import <Foundation/Foundation.h>
#import "Car.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Car* myCar = [[Car new] autorelease];
[myCar performSelectorInBackground:#selector(LoopAndSay) withObject:nil];
for(int i = 0; i < 100; i++ )
{
NSLog(#"Main loop on %i", i);
}
}
return 0;
}
Car.m
#import "Car.h"
#implementation Car
#synthesize name, model;
-(void) LoopAndSay {
for(int i = 0; i < 100; i++)
{
NSLog(#"Looping for the %i time", i);
}
}
#end
Now, if I run it as is the background loop will sometimes not complete(stopping between iterations 94 and 97). Additionally, if I switch my code around so that the background loop is not called until after the main thread loop then it will not run any iterations.
Is this because the main thread is finished and does not want to wait for the background thread to run to completion? If this is the case, is there a way to force the program to continue running until both the main thread and any background threads are complete?
This is almost certainly because you exit the scope of the autorelease pool before the thread completes your main function returns before the background thread completes. Try adding sleep(3) or something before its end and see if your background thread completes.
On Mac OS X, a process terminates when all its "foreground" threads have completed. peformSelectorInBackground: only creates a background thread, so once main returns, you're left with no foreground thread and your process is terminated.
This is because all threads created with performSelectorInBackground:... and other Objective-C APIs are detached, so that the program can terminate without waiting for them to finish. From the Threading Programming Guide:
At application exit time, detached threads can be terminated immediately but joinable threads cannot. [...] If you do want to create joinable threads, the only way to do so is using POSIX threads...
In this case, your main thread loop happens to finish a bit sooner than the background thread, so the background thread loop doesn't run through to the end. Instead of using POSIX threads (which is much less convenient), you could also use NSOperationQueue, which easily allows you to wait for all operations in a queue to finish.

High CPU consumption and latency while reading serial data

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.

Writing in a file from multiple threads

I'm writing a download manager in Objective-C which downloads file from multiple segments at the same times in order to improve the speed. Each segement of the file is downloaded in a thread.
At first, I thought to write each segment in a different file and to put together all the files at the end of the download. But for many reasons, it's not a good solution.
So, I'm searching a way to write in a file at a specific position and which is able to handle multiple thread because in my application, each segment is downloaded inside a thread.
In Java, I know that FileChannel does the trick perfectly but I have no idea in Objective-C.
The answers given thus far have some clear disadvantages:
File i/o using system calls definitely has some disadvantages regarding locking.
Caching parts in memory leads to serious issues in a memory constrained environment. (i.e. any computer)
A thread safe, efficient, lock free approach would be to use memory mapping, which works as follows:
create the result file of (at least) the total length needed
open() the file for read/write
mmap() it to some place in memory. The file now "lives" in memory.
write received parts in memory at the right offset in the file
keep track if all pieces have been received (e.g. by posting some selector on the main thread for every piece received and stored)
munmap() the memory and close() the file
The actual writing is handled by the kernel - your program will never issue a write system call of any form. Memory mapping generally has little downsides and is used extensively for things like shared libraries.
update: a piece of code says more than 1000 words... This is the mmap version of Mecki's lock-based multi-thread file writer. Note that writing is reduced to a simple memcpy, which cannot fail(!!), so there is no BOOL success to check. Performance is equivalent to the lock based version. (tested by writing 100 1mb blocks in parallel)
Regarding a comment on "overkill" of an mmap based approach: this uses less lines of code, doesn't require locking, is less likely to block on writing, requires no checking of return values on writing. The only "overkill" would be that it requires the developer to understand another concept than good old read/write file I/O.
The possibility to read directly into the mmapped memory region is left out, but is quite simple to implement. You can just read(fd,i_filedata+offset,length); or recv(socket,i_filedata+offset,length,flags); directly into the file.
#interface MultiThreadFileWriterMMap : NSObject
{
#private
FILE * i_outputFile;
NSUInteger i_length;
unsigned char *i_filedata;
}
- (id)initWithOutputPath:(NSString *)aFilePath length:(NSUInteger)length;
- (void)writeBytes:(const void *)bytes ofLength:(size_t)length
toFileOffset:(off_t)offset;
- (void)writeData:(NSData *)data toFileOffset:(off_t)offset;
- (void)close;
#end
#import "MultiThreadFileWriterMMap.h"
#import <sys/mman.h>
#import <sys/types.h>
#implementation MultiThreadFileWriterMMap
- (id)initWithOutputPath:(NSString *)aFilePath length:(NSUInteger)length
{
self = [super init];
if (self) {
i_outputFile = fopen([aFilePath UTF8String], "w+");
i_length = length;
if ( i_outputFile ) {
ftruncate(fileno(i_outputFile), i_length);
i_filedata = mmap(NULL,i_length,PROT_WRITE,MAP_SHARED,fileno(i_outputFile),0);
if ( i_filedata == MAP_FAILED ) perror("mmap");
}
if ( !i_outputFile || i_filedata==MAP_FAILED ) {
[self release];
self = nil;
}
}
return self;
}
- (void)dealloc
{
[self close];
[super dealloc];
}
- (void)writeBytes:(const void *)bytes ofLength:(size_t)length
toFileOffset:(off_t)offset
{
memcpy(i_filedata+offset,bytes,length);
}
- (void)writeData:(NSData *)data toFileOffset:(off_t)offset
{
memcpy(i_filedata+offset,[data bytes],[data length]);
}
- (void)close
{
munmap(i_filedata,i_length);
i_filedata = NULL;
fclose(i_outputFile);
i_outputFile = NULL;
}
#end
Queue up the segment-objects as they are received to a writer-thread. The writer-thread should keep a list of out-of-order objects so that the actual disk-writing is sequential. If a segment download fails, it can be pushed back onto the downloading thread pool for another try, (perhaps an internal retry-count should be kept). I suggest a pool of segment-objects to prevent one or more failed download of one segment resulting in runaway memory use as later segments are downloaded and added to the list.
Never forget, Obj-C bases on normal C and thus I would just write an own class, that handles file I/O using standard C API, which allows you to place the current write position anywhere within a new file, even far beyond the current file size (missing bytes are filled with zero bytes), as well as jumping forward and backward as you wish. The easiest way to achieve thread-safety is using a lock, this is not necessary the fastest way but in your specific case, I bet that the bottleneck is certainly not thread-synchronization. The class could have a header like this:
#interface MultiThreadFileWriter : NSObject
{
#private
FILE * i_outputFile;
NSLock * i_fileLock;
}
- (id)initWithOutputPath:(NSString *)aFilePath;
- (BOOL)writeBytes:(const void *)bytes ofLength:(size_t)length
toFileOffset:(off_t)offset;
- (BOOL)writeData:(NSData *)data toFileOffset:(off_t)offset;
- (void)close;
#end
And an implementation similar to this one:
#import "MultiThreadFileWriter.h"
#implementation MultiThreadFileWriter
- (id)initWithOutputPath:(NSString *)aFilePath
{
self = [super init];
if (self) {
i_fileLock = [[NSLock alloc] init];
i_outputFile = fopen([aFilePath UTF8String], "w");
if (!i_outputFile || !i_fileLock) {
[self release];
self = nil;
}
}
return self;
}
- (void)dealloc
{
[self close];
[i_fileLock release];
[super dealloc];
}
- (BOOL)writeBytes:(const void *)bytes ofLength:(size_t)length
toFileOffset:(off_t)offset
{
BOOL success;
[i_fileLock lock];
success = i_outputFile != NULL
&& fseeko(i_outputFile, offset, SEEK_SET) == 0
&& fwrite(bytes, length, 1, i_outputFile) == 1;
[i_fileLock unlock];
return success;
}
- (BOOL)writeData:(NSData *)data toFileOffset:(off_t)offset
{
return [self writeBytes:[data bytes] ofLength:[data length]
toFileOffset:offset
];
}
- (void)close
{
[i_fileLock lock];
if (i_outputFile) {
fclose(i_outputFile);
i_outputFile = NULL;
}
[i_fileLock unlock];
}
#end
The lock could be avoided in various way. Using Grand Central Dispatch and Blocks to schedule the seek + write operations on a Serial Queue would work. Another way would be to use UNIX (POSIX) file handlers instead of standard C ones (open() and int instead of FILE * and fopen()), duplicate the handler multiple times (dup() function) and then placing each of them to a different file offset, which avoids further seeking operations on each write and also locking, since POSIX I/O is thread-safe. However, both implementations would be somewhat more complicating, less portable and there would be no measurable speed improvement.