Friday, January 4, 2008

Apple Event nesting, Mail no likey


My continued experiments in scripting (in this case Mail) have thrown up an interesting issue.
If I get Mail to run an applescript that tells my app about the arrival of some new mail, and then attempt to read this new mail from my application in this call, all sorts of things go wonky.

The most basic failure is that my app fails to 'see' the newly arrived mail when asking Mail. It's as if Mail is not equipped to handle apple events at some times (such as when processing rules), or perhaps this is a more general problem with Apple Events on the Mail process being nested.

The thought had occurred to me while writing the scripting bridge code that it may not be possible to nest an Apple Event connection to a given process inside another connection from that same processes - though I was not able to find any documentation that explicitly told me NOT to do this.

Aside from simply not being able to query Mail as I'd expect, I've experienced a range of other nasties that may be related, including:
1. Apparent lock-ups of my app (but not every time)
2. ...inability to stop the app as a debuggee within XCode
3. ...and (after 2) a full kernel panic

The fix seems to be to avoid the nesting. Simply 'step out of the way' of the original Apple Event from Mail and let it complete before sending any messages the other way on a completely different stack (i.e. 'later').

I'm pretty sure the kernel panic was due to a combination of sitting on a break point while handling the Apple Event from the Mail process, then (possibly after some time-out), attempting to kill the debuggee while some low-level IPC code thought some resources were still being locked between the two processes.

It would seem more likely that the problem I'm experiencing would be an issue with the internal state of Mail at the time it processes mail rules, rather than a general problem nesting Apple Events between processes (which seems rather fundamental for a general IPC mechanism). As a newbie, I need to do some more research...


has said...

"If I get Mail to run an applescript that tells my app about the arrival of some new mail, and then attempt to read this new mail from my application in this call, all sorts of things go wonky."

Sounds like you've got yourself a contention issue.

Mail runs the AppleScript on the main thread, which is also responsible for running the main event loop. Since the main thread is blocked while the AppleScript is executing, Mail can't handle any external events until after the AppleScript returns.

In your case, your AppleScript can't return until it receives a reply from your external application. In turn, your external application can't return until it receives a reply to the event it sent to Mail - but until your AppleScript returns, that event is stuck in Mail's event queue. Result: deadlock.

How to break this deadlock will depend on what it is you're trying to do. Assuming that the AppleScript's sole function is to notify your application when new mail arrives, it doesn't actually need to wait for your application to process those mails before returning, so here's two potential solutions:

1. Your AppleScript could wrap an 'ignoring application responses' block around the commands it sends to your application. This is the simplest to do, but has the slight disadvantage that this also suppresses any errors that might arise while events are being sent, so is no good if your script needs to provide any sort of error handling on those commands (e.g. displaying a dialog if your application isn't found).

2. Your application could process the incoming events non-synchronously, assigning incoming jobs to a background thread or pushing them into an internal queue for later processing on the main thread, and returning immediately.



has said...

p.s. The AppleScript-implementors mailing list is your friend:

Luke Evans said...

Hi has,

>Sounds like you've got yourself a contention issue

It's not a classic deadlock though. For the most part, the behaviour was that Mail replies to all the events, but fails to return mails that one would expect to be present (which have just arrived and are having their rules processed). Nevertheless, this is clearly related to the 'internal state' of Mail, and as you allude, to the fact that Mail is blocked while dealing with the Apple Events.

Processing the events non-synchronously did solve the problem (as I noted in my post), and I achieved this by performing my mail scan on a timer (I also wanted to consolidate multiple notifications of new mail into a single scan event, which this accomplishes nicely).


Thanks for the tip. I probably should subscribe :-)