Thursday, April 19, 2007

Aperture2Gmail v0.2 Now Public

Late at night last Sunday, I put the wraps on Aperture2Gmail v0.2 and released it the following Monday. It's up on the iPhoto2Gmail site -- might have to change that name now. There have been a few downloads already, but mostly, things are quiet. And that's good, because I don't have a working copy of Aperture with which to continue dev. work for the moment :) Luckily, I had enough free time during the 30 day trial to make a version I felt was good enough to release.

It's also good because I have been really busy at work and also juggling phone interviews.

The busy part is especially a bummer because I have a neat idea that I'd like to hack on... oh well, that'll just have to wait for now. I'll give you a hint, though, it involves YUI.

I'd like to thank the testers that volunteered their time and CPU cycles to help me get it out the door on time. You guys rock!

Friday, April 13, 2007

Sponsor a Feature and Custom Dev

I'd like to announce that I am available for custom software development work on either iPhoto2Gmail, Aperture2Gmail or other interesting plugins and projects that may involve PHP, Objective C/Cocoa, Javascript, Java, etc.

For example, if there is a feature you would like to see in iPhoto2Gmail really really badly, we can discuss it, come to an agreement and I can code it for you. This assures you get desired functionality quickly, and I can pay for rent!

Thursday, April 5, 2007

NSTasks, NSPipes, and deadlocks when reading...

I used NSTask and NSPipe in iPhoto2Gmail from the very beginning... it is how all of the "hard work" is done. All information I send and receive from "the cloud" (as it is now called I'm told) is via NSTasks. I use the standard method of setting up an NSPipe to stdout and stderr and then a file handle on that pipe for reading.

This worked great until a few days ago when I got two consecutive bug reports about ip2g hanging when retrieving... hrmmm... Thousands of downloads for version v0.4 and only three folks contacted me with a similar hang. But still, this bugged me. A lot. I wrapped the php code for retrieving contacts in an AppleScript and sent it to a couple of willing guys for testing. Surprisingly, they both reported that the contacts came down just fine.

Darn, I was hoping it would be a simple php issue, I really didn't feel like debugging NSTasks and NSPipes! I went back and searched my emails to see what these guys might have in common and it was only one thing: both had G4 based Macs. Could it be an architecture issue? Nah. Then a light came on -- kind of dim, but there: single processor! I shut down one of the cores on my mini and busted out the debugger. Sure enough, NSTask was hanging.

Many hours later scouring mailing list archives, google, and cocoadev and I had what I thought was a working solution... then I added some contacts to my Gmail Contact list and ran into the hang again!

By this time, I was well aware that NSPipes block when they get full, and when paired with a waitUntilExit call on the NSTask you get yourself a nasty deadlock when calling readToEndOfFile or availableData on the file handle. This condition wasn't straightforward for me to fix because in most cases the deadlock would_not_ occur with the presence of the second core; or when my internet connection was slow enough that the pipe never got full before the first pass of the availableData loop.

When I implemented a while (availableData) loop like the Apple Moriarity example, I kept getting [NSConcreteFileHandle availableData]: Interrupted system call"] exceptions. Argh! Thankfully, Chris Suter was awesome enough to post his workaround for this to Cocoa-dev mailing list for devs like me to find and rejoice over.


The final solution for emptying the pipe as it becomes full on machines with any number of cores and any network bandwidth looked like this:


{
...
data = [self availableDataOrError: file ];
while ( [data length] > 0) {
myString = [myString stringByAppendingString: [[[NSString alloc]
initWithData:data encoding:NSUTF8StringEncoding] autorelease]];
data = [self availableDataOrError: file ];
}
[file closeFile];
[task release];
return lines;
}

//slightly modified version of Chris Suter's category function
//used as a private method
-(NSData *) availableDataOrError: (NSFileHandle *)file {
for (;;) {
@try {
return [file availableData];
} @catch (NSException *e) {
if ([[e name] isEqualToString:NSFileHandleOperationException]) {
if ([[e reason] isEqualToString: @"*** -[NSConcreteFileHandle availableData]: Interrupted system call"]) {
continue;
}
return nil;
}
@throw;
}
}

}



Note that I no longer needed to waitUntilExit on the NSTask, since this is a synchronous operation.