The IMAP folder compaction functions are a little odd in that they attempt to perform an IMAP Expunge as well as a compact.
The compact() and compactAll() functions can be passed an
nsIUrlListener to signal when they complete. There is an attempt in the code to make sure this is only called after both the expunge and compaction are completed, but this is incomplete.
There's a lot of opportunity this to get out of sync, and for listeners to be called more than once.
For compaction, the listener is passed directly to the foldercompaction code, which calls it when done (without any consideration of an ongoing expunge).
For the expunge, the listener is stashed on the folder
m_UrlListener member, and the folder itself is passed as a listener to the Expunge operation. The folders own
OnStopRunningUrl() is then called, the url checked, and if it was an expunge operation, there's a hack in there to call
m_UrlListener, but only if there's no ongoing compaction still running. Basically, it's a bit of a mess - even more so for the multi-folder
I think there are three alternative approaches that could be taken:
- route both the compact operation and the expunge through the folder's own
OnStopRunningUrl(), and decide there when to call the listener passed in to
Compact() (i.e. once both compaction and expunge are complete). This is tricky, because the compaction is not an IMAP protocol operation, and there's no URL associated with it, so the folders
OnStopRunningUrl() can't easily identify the compaction completion.
- Use an separate nsIUrlListener which synchronises it all (probably using C++ closures to keep the completion logic near to the site of the initiation - a C++ equivalent of
- punt on this and turn Compact and Expunge into separate operations (see Bug 1130277). Probably letting the GUI code handle waiting for them both to finish (using
I lean toward 3, with a dash of 2 if this actually shows up as a problem in the meantime.