Skip to content

Commit 0b2b5ae

Browse files
committed
imap: resolve unstable EXPUNGE observability
Following the issuance of the EXPUNGE IMAP command by a client, and after imapd has deleted e.g. n=7 mails at once via the midb M-DELE command, imapd would erratically respond with fewer than n EXPUNGE responses, deferring the remaining EXPUNGE responses until the next opportunity. Even though EXPUNGE events were sent to other imapd threads, the current thread was skipped, so it had to rely on loopback events — which do not arrive timely.
1 parent ebbff8d commit 0b2b5ae

File tree

2 files changed

+27
-11
lines changed

2 files changed

+27
-11
lines changed

mra/imap/imap_cmd_parser.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2298,6 +2298,7 @@ int imap_cmd_parser_append(int argc, char **argv, imap_context *pcontext) try
22982298
auto imap_reply_str1 = resource_get_imap_code(1715, 2);
22992299
std::string buf;
23002300
for (i=0; i<10; i++) {
2301+
// wait for midb's actions showing up... woah terrible
23012302
uint32_t uidvalid = 0;
23022303
if (system_services_summary_folder(pcontext->maildir,
23032304
temp_name, nullptr, nullptr, nullptr, &uidvalid, nullptr,

mra/imap/imap_parser.cpp

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,28 @@ static std::vector<imap_context *> *sh_query(const char *x)
11101110
return i == g_select_hash.end() ? nullptr : &i->second;
11111111
}
11121112

1113+
/*
1114+
* The event bus does not loopback messages to the same connection id (PID),
1115+
* so, generally, anything that needs to be conveyed to imapd sibling threads
1116+
* we have to do via shared memory. Fair enough.
1117+
*
1118+
* But there is a caveat: exmdb actions loop do back as notifications to midb,
1119+
* which in turn goes back imapd, like so:
1120+
*
1121+
* - imapd issues M-DELE command to midb
1122+
* - midb issues delete_message EXRPC to exmdb
1123+
* - exmdb notifies midb due to a mailbox-wide subscription midb asked for
1124+
* - midb issues MESSAGE-EXPUNGED on the event bus
1125+
* - imapd receives MESSAGE-EXPUNGED event
1126+
*
1127+
* imap:APPEND commands actually behaves similar, leading to a
1128+
* FOLDER-TOUCH event coming back.
1129+
*
1130+
* Notifications arrive asynchronously though, and thus with a fair chance to
1131+
* be "late", such that the EXPUNGE command would respond with fewer EXPUNGE
1132+
* responses than the number of messages to delete.
1133+
*/
1134+
11131135
void imap_parser_bcast_touch(const imap_context *current, const char *username,
11141136
const char *folder)
11151137
{
@@ -1196,28 +1218,21 @@ void imap_parser_bcast_expunge(const imap_context &current,
11961218
char user_lo[UADDR_SIZE];
11971219
gx_strlcpy(user_lo, current.username, std::size(user_lo));
11981220
HX_strlower(user_lo);
1199-
/*
1200-
* gromox-event does not send back events to our own process.
1201-
* (So, we have to edit the data structures for our other threads
1202-
* from this function.)
1203-
* Benefit: no IPC roundtrip to notify other threads.
1204-
* Downside: Some code duplication between imap_parser_event_expunge
1205-
* and imap_parser_bcast_expunge.
1206-
*/
1221+
1222+
/* Distribute expunges via shared memory as mentioned earlier. */
12071223
std::unique_lock hl_hold(g_hash_lock);
12081224
auto ctx_list = sh_query(user_lo);
12091225
if (ctx_list == nullptr)
12101226
return;
12111227
for (auto &other : *ctx_list) {
1212-
if (&current == other ||
1213-
strcmp(current.selected_folder, other->selected_folder) != 0)
1228+
if (strcmp(current.selected_folder, other->selected_folder) != 0)
12141229
continue;
12151230
for (auto p : exp_list)
12161231
other->f_expunged_uids.emplace_back(p->uid);
12171232
other->async_change_mask |= REPORT_EXPUNGE;
12181233
}
12191234
hl_hold.unlock();
1220-
/* Bcast to other entities */
1235+
/* Bcast to other bus listeners (IOW, pop3) */
12211236
auto cmd = "MESSAGE-EXPUNGE "s + user_lo + " " + current.selected_folder + " ";
12221237
auto csize = cmd.size();
12231238
for (auto p : exp_list) {

0 commit comments

Comments
 (0)