Archive for February, 2009

Safari 4 beta and GrowlMail

Tuesday, February 24th, 2009

The problem

The WebKit framework that comes with Safari 4 enforces a restriction on calling WebKit from a secondary thread.

GrowlMail accesses message contents on a secondary thread because they may not be in yet. Accessing message contents can result in a call to WebKit. That results in a crash with the new WebKit.

The fix (section added 2009-04-19, updated 2009-06-22 for the 1.1.5 release)

Download Growl 1.1.5 and install GrowlMail 1.1.5.
(Make sure you install GrowlMail specifically! The Growl Installer package does not include GrowlMail or any of the other Extras.)

The workaround

UPDATE 2009-06-22: You don't need to do this anymore. Install GrowlMail 1.1.5 instead.

If you're fast or can easily turn off your internet connection first, go to GrowlMail's Preferences and set GrowlMail to summary mode. (You might also try putting Mail into Offline mode immediately after launching it until you've made this change.)

  1. Go to Mail's Preferences.
  2. Click on the chevron button at the far right end of the toolbar.
  3. Click on GrowlMail.
  4. Choose the second of the three radio buttons: “Show a summary of received emails”.

You can then go back into online mode/turn your internet connection back on/rest easy.

The other way to do it is to run this command in a terminal window:

defaults write com.apple.mail GMSummaryMode -int 2

Ordinarily, I'd tell you to quit Mail first, but since Mail will unexpectedly quit the first time tries to Growl about a new mail message, I don't think I need to bother. ☺

The fix

We're not sure about that yet.

The problem is that when Mail adds a new message to your library, it may not have fully downloaded it yet. (You can see this yourself sometimes when your internet connection is slow or under heavy load: You'll click on a message and see a “Loading” screen in the preview pane.)

When that happens, if we try to get the message body on the main thread, it blocks the UI until the body arrives. So we do it on a secondary thread instead. That's now a problem.

The ideal fix is that we find a way to determine whether the body has come in yet. If it hasn't, we could set a timer for a few seconds, then check again then and post a notification with “body not loaded yet” if it's still not in.

Another fix I would consider is simply killing the feature that shows the message body in the notification. I'm sure a lot of you like it, but if it breaks the app and there's no fix, then it has to go.

UPDATE 2009-02-25: As it turns out, we had most of the above fix in already (except that we were using a delay, not a timer—not a problem, since it's on its own thread). So the fix was simply to move most of the code to the main thread. I've done that and it works. The relevant patches are pending review; if I hear nothing bad about them from the other developers by tomorrow, I'll make them permanent commits and push them to both repositories, where they will be part of 1.1.5.

Other points

This is not a bug in WebKit. Strictly speaking, it's not a bug in GrowlMail, either, because it's not like we're disobeying the documentation for Mail's API (there is none!).

Apple changed WebKit to throw this exception, and GrowlMail doesn't catch it. As far as I'm concerned, it's GrowlMail's fault and we're the ones who need to fix it.

Timeline? No idea. I do intend to have this fix in for 1.1.5, since Safari 4 will probably be either out or coming Real Soon Now by then. (This among other important fixes, such as the off-by-two error in the Growl prefpane.)

UPDATE 2009-06-22: The fix is in GrowlMail 1.1.5, which we released on 2009-06-16.

Why the compiler won’t let you declare a variable immediately after a case

Friday, February 20th, 2009

Consider this code:

enum { foo, bar, baz } my_var;
switch (my_var) {
    case foo:
        int foo_alpha; //Line 7
        int foo_beta;
        break;

    case bar:
        my_var = baz;
    case baz:
        printf("Bar or baz encountered\n");
        break;
}

Try to compile it as C99.

Here's what GCC says:

test.c: In function ‘main’:
test.c:7: error: syntax error before ‘int’

What!

First off, why not? What is invalid about this declaration statement?

Second, why is foo_alpha invalid and not foo_beta?

There are several definitions in the C99 specification that come together to cause this problem.

The first is that there is no such thing as a declaration statement, because declarations are not statements. See §6.7 and §6.8; note that neither definition includes the other. In the language that is C99, declarations and statements are separate concepts.

The second is the definition of a compound statement. The definition of a switch statement (which is part of §6.8.4) is:

switch ( expression ) statement

If you go back up to §6.8, you'll see that another possible kind of statement is a compound statement, for which §6.8.2 gives this definition:

compound-statement:
  • {   block-item-listopt   }
block-item-list:
  • block-item
  • block-item-list   block-item
block-item:
  • declaration
  • statement

So a declaration is not a statement, a compound statement can contain declarations and/or statements, and a switch statement is a prefix upon (usually) a compound statement.

Now, the kicker. Read the relevant definition of a labeled statement from §6.8.1:

case   constant-expression   :   statement

Statement. Not block-item. Not declaration. Statements only.

So this is what the compiler sees in valid code (with a declaration not following a case label):

  • selection statement (switch)
    • compound statement
      • labeled statement
        • statement
      • statement
      • statement
      • declaration
      • statement
      • jump statement (break)
      • labeled statement
      • statement
      • jump statement (break)
      • labeled statement
      • statement
      • jump statement (break)

Now, consider how the compiler sees my code above:

  • selection statement (switch)
    • compound statement
      • labeled statement
        • declaration — wait, this isn't a statement! ERROR
      • declaration — also a kind of block-item, so it's perfectly valid here
      • jump statement (break)
      • labeled statement
      • statement
      • labeled statement
      • statement
      • jump statement (break)

I hope this makes clear that this isn't a compiler bug; the C99 language really does work this way.

(One possible solution would be to make a declaration a kind of statement, but I don't know what other ramifications that might have. [UPDATE 11:36: Jeff Johnson tells us why not.])

Half-Life 2 Photography

Thursday, February 19th, 2009

My challenge to you:

Take a screenshot in any of the Half-Life 2 games (including Portal), and make it look and feel like an artistic or journalistic photograph. (I don't mean filters; I mean framing and the scene itself.)

Here's my first contribution:

A scene of the observer looking out a grimy window on to a Combine autogun emplacement.

If you want to join in, take a photographic-looking screenshot, and submit it to the Flickr group.

Manpage Monday: CC_SHA(3cc)

Monday, February 16th, 2009

The Common Crypto library in Mac OS X 10.4 and later provides simple APIs for five SHA algorithms:

CC_SHA1() computes the SHA-1 message digest of the len bytes at data and places it in md (which must have space for CC_SHA1_DIGEST_LENGTH == 20 bytes of output). It returns the md pointer.

CC_SHA1_Init() initializes a CC_SHA1_CTX structure.

CC_SHA1_Update() can be called repeatedly with chunks of the message to be hashed (len bytes at data).

CC_SHA1_Final() places the message digest in md, which must have space for CC_SHA1_DIGEST_LENGTH == 20 bytes of output, and erases the CC_SHA1_CTX.

The successor versions of SHA-1, SHA-2, are also implemented for hash bit lengths of 224, 256, 384, and 512. The functions to call to invoke the larger hash-size versions of the algorithms include the hash size as part of the function names: …

RETURN VALUES

All routines return 1 except for the one-shot routines ( CC_SHA1(), etc.), which return the pointer passed in via the md parameter.

Bad Behavior has blocked 250 access attempts in the last 7 days.