About | Buy the Software | Forum | Industry Watch | Learning Curve | Newsletters | Products
Home » Learning Curve » Hotspots

Apple's Exceptional Behaviour

One can't assume the sky isn't falling.


Buy It

Try It

Following up on good tidings from 4 November and the first of yesterday's good tidings and the second of yesterday's good tidings there's an obvious need to dig even deeper. There's namely concern the same type of (lack of) thinking that leads to the 'massive data loss' catastrophes is found here as well.

And it is. Sit tight.

Exception Handling

The unexpected can happen on any system at any time. This 'exceptional behaviour' is what's behind 'structured exception handling': the need - to be able to deal with the unexpected.

Device drivers can report anomalies at any time. Such reports are propagated up through the system to user land code so applications and users in effect can have the opportunity to deal with them.

Blackouts, brownouts, physical disk errors, and so forth: these are not programmatic errors but they do cause errors. Users need to know that their file operations succeed - not just that their application code has performed correctly.

The 'massive data loss' catastrophe was a prime example of this: Apple code was reading massive files into memory, removing the files before the copy operation was completed and checked for errors, leaving trusting admins with massive data losses.

Obviously the proper way to perform such an operation involved not touching the source until it's been checked the destination arrived where it should be and is intact.

Convinced the sky could never fall on the Apple World, the Apple engineers just assumed all would work well. Tantamount to a computer science felony.

But this 'the sky can't fall' thinking is more pervasive at Apple than previously suspected. It also infects the code behind ordinary user file operations used in thousands of document editing applications available for the platform.

The Role of the Document Controller

The NeXTSTEP/OPENSTEP NSDocumentController is one of the things that makes the platform great. The client application is freed from the tedium (and redundant code) to keep track of file names and sundry information and can leave all of that to the document controller. The document controller tracks open documents, unsaved changes to documents, prompts to open files, prompts to save files, open new files, and so forth.

It's object orientation at its best - provided it's written the right way.

In the previous three articles in this series we've seen there are some funny things happening with this document controller, most noticeably in the strange implementation for Leopard. We've also seen that some of this unsafe behaviour actually dates back even farther - to Tiger and most likely Panther and Jaguar before it.

Defensive Programming

The overriding principle of good programming is to be defensive - to never trust anyone else's code, to always check things are done correctly, to regularly perform so-called 'sanity checks' to make sure the data you're being sent actually makes sense.

All good code is written defensively. Nothing may ever be taken for granted. One may never assume the sky is not falling.

Below this level of user land caution one has - hopefully - a good layer of structured exception handling to catch errors that are without the realm of the software itself.

All these precautions are necessary to safeguard and protect user data.

Exception Handling & Apple

Recreating the scenario found in the 'massive data loss' catastrophes won't be necessary to illustrate the following point. The evidence will still prove that if there were exceptional things happening to your file system Apple's document controller would be blissfully ignorant anyway.

We can proceed as in the previous examples by interception document controller calls and playing a bit with them to see how the document controller reacts. There are three ways we might do this.

Method one. Write to the file as requested but then remove all file permissions.

-(BOOL)writeToFile:(NSString *)file ofType:(NSString *)type {
    BOOL f = [textString writeToFile:file atomically:0 encoding:encoding error:0];
    chmod([file UTF8String], 0);
    return f;
}

Method two. Write to the file as requested but then simply remove the file.

-(BOOL)writeToFile:(NSString *)file ofType:(NSString *)type {
    BOOL f = [textString writeToFile:file atomically:0 encoding:encoding error:0];
    unlink([file UTF8String]);
    return f;
}

Method three. Change the path of the given file prior to writing to it.

-(BOOL)writeToFile:(NSString *)file ofType:(NSString *)type {
    return [textString writeToFile:
        [file stringByAppendingPathExtension:@"exception"] atomically:0 encoding:encoding error:0];
}

In all three cases a file is actually written to but the file NSDocumentController is expecting either won't be there or won't be readable.

Why try this hanky-panky you might ask? Easy: to see if the document controller checks the source file before it obliterates your own (destination) file. To emulate what would happen if a 'real' exception occurred.

The proper way to handle this situation is as follows.

  1. If you really feel you have to - dubious in this scenario: create a new temporary path, send this path to the document and tell it to write the file contents there.

  2. As you're being blocked by the message you sent to the document: wait (obviously) for the return value from the document. If the document says it failed to write the file then just drop it. But if the document says it succeeded - check the file. Check for its existence by trying to open the file (with an exclusive lock) for reading.

  3. If and only if you succeed in step #2 can you proceed to open the destination for writing.

You should never remove the destination file - not even when you're replacing it. You simply open it to create if necessary and truncate its length to zero.

But here's the way Apple do it.

  1. If the user reply to the 'replace' prompt it to proceed remove the destination file right now no matter what without checking a damned thing.

  2. User's original file is now gone and cannot be recovered.

  3. Ask the client application to save the file to the arbitrary location and hope for the best.

Any type of 'exceptional behaviour' - disk crash, brownout, blackout, another interfering process, etc - and neither copy of your file - neither the old one or the new one you thought you'd saved - will remain.

Of course you might want to go hunting for the debris left behind by Apple. And you might just find a byte or two of your file. But that hardly qualifies as user-friendly, 'it just works', or industrial strength professional code.

See Also
Hotspots: Leopard -Tmp-
Developers Workshop: Y.G.B.K.
Hotspots: A Document Being Saved on 10.4
Hotspots: Just An Ordinary Innocent Little Old Text File
Hotspots: (A Document Being Saved By Rixedit)/Untitled
Rixstep/7: 'Apple's Exceptional Behaviour' (Membership Required)
Rixstep/7: 'A Document Being Saved on 10.4' (Membership Required)
Hotspots: (A Document Being Saved By Rixedit)/CocoaDocument-Based.rtx
Rixstep/7: '(A Document Being Saved By Rixedit)/Untitled' (Membership Required)
Rixstep/7: '(A Document Being Saved By Rixedit)/CocoaDocument-Based.rtx' (Membership Required)

About | Buy the Software | Forum | Industry Watch | Learning Curve | Newsletters | Products
Copyright © Rixstep. All rights reserved.