Mystery Bug: Heisenberg’s Uncertainty Principle

I’ve spent the past couple hours going crazy over a bug. It’s the type of bug where it breaks except for when you look at it, in which case, it works all the time. Don’t believe me? Compile the following program:

#import <Foundation/Foundation.h>

int main(int argc, const char *argv[])
{
    NSAutoreleasePool     *pool;
    NSString              *path;
    NSDictionary          *dict;
  
    pool = [[NSAutoreleasePool alloc] init];
    path = [NSString stringWithUTF8String:argv[1]];
  
    dict = [[NSFileManager defaultManager] fileAttributesAtPath:path
                                         traverseLink:NO];

    // The busy flag will be NULL if you comment out the next line.
    //NSLog(@"DICT: %@", dict);

    NSLog(@"Creation: %@", [dict objectForKey:NSFileCreationDate]);
    NSLog(@"Busy: %@", [dict objectForKey:NSFileBusy]);
}


By the way, I know I’m not releasing the pool.

Now, take a file and set its busy flag. You can do this as follows:

/Developer/Tools/SetFile -a Z some file

Then run my little program with some file as an argument. Notice how the busy flag is null?

Well, let’s see what’s in the dictionary passed back to us to find out what’s going wrong here. Edit the program above to uncomment the NSLog statement. Run it again. Notice how the busy flag is now set?

It appears that the dictionary is half-initialized until you happen to call certain methods (-allKeys works as well as -description). I haven’t tested every key but it does seem to be peculiar to NSFileBusy.

For the time being, I’m going to switch to Carbon to do this but I’d love to hear theories (or even facts) on what’s going on here. Let the wild speculation begin.

Oh, and since I brought up Heisenberg, I can’t let an opportunity to include an artful rendering of a dead cat pass by so here:

dead cat

Category: Carbon, Cocoa, Debugging, OS X, Programming 6 comments »

6 Responses to “Mystery Bug: Heisenberg’s Uncertainty Principle”

  1. Chris Ryland

    Wow, no idea. The only thing that’s suspicious is that NSFileBusy was added in 10.4. Maybe it’s buggy? 😉

  2. Chris Suter

    I think it’s sometehing to do with the fact that you don’t actually get an NSDictionary back. You get an instance of NSFileAttributes back and they’ve overridden objectForKey in that class.

    Looking at the structure of NSFileAttributes and from a bit of playing, it looks like when you ask for objectForKey, it will generate a Cocoa object on the fly from other data structures behind the scenes.

    It also seems to cache the dictionary when generated by calling something like keyEnumerator on the dictionary (which is what would get called if you call keys, or description). I’m guessing that it only gets NSFileBusy from the dictionary and isn’t able to generate that on the fly.

    You should file a bug.

  3. mr_noodle

    Ah, that makes sense. Makes it so that you don’t have to fetch all the different attributes up front which means less of a performance hit if you are only looking at one attribute, for instance. Thanks for performing the dissection.

    In any case, bug already filed: rdar://5047616

    Never really understood the use of those urls since only Apple people can use them directly.

  4. Jon H

    I think you mean ‘Heisenbug’

  5. Berle

    Good insight Chris! I always wondered if traversing a directory tree and getting the file attributes of every file would mean a huge performance hit, but obviously Apple leaves us only a meagre margin for optimization there.

    It’s interesting to note that there is a – (NSDate *)fileCreationDate in NSDictionary but no – (BOOL)fileBusy method. Did someone at Apple only do half of the job?

  6. John G

    For some reason that makes me think of a certain Schrödinger rather than Heisenberg 🙂 Is there such a thing as a Schröderbug? 🙂


Leave a Reply



Back to top