From: Timo Sirainen Date: Tue, 24 Nov 2015 11:41:58 +0000 (+0200) Subject: imap: Added extra assert checks to make sure command states are consistent. X-Git-Tag: 2.2.20.rc1~37 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=117bc062ec4a3770f6e4291e4697895d66d0f18a;p=thirdparty%2Fdovecot%2Fcore.git imap: Added extra assert checks to make sure command states are consistent. --- diff --git a/src/imap/imap-client.c b/src/imap/imap-client.c index 34b6e9ae1e..6ada93d590 100644 --- a/src/imap/imap-client.c +++ b/src/imap/imap-client.c @@ -799,37 +799,84 @@ void client_command_free(struct client_command_context **_cmd) } } -void client_continue_pending_input(struct client *client) +static void client_check_command_hangs(struct client *client) { - i_assert(!client->handling_input); + struct client_command_context *cmd; + unsigned int unfinished_count = 0; + bool have_wait_unfinished = FALSE; + + for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) { + switch (cmd->state) { + case CLIENT_COMMAND_STATE_WAIT_INPUT: + i_assert(client->io != NULL); + unfinished_count++; + break; + case CLIENT_COMMAND_STATE_WAIT_OUTPUT: + i_assert((io_loop_find_fd_conditions(current_ioloop, client->fd_out) & IO_WRITE) != 0); + unfinished_count++; + break; + case CLIENT_COMMAND_STATE_WAIT_EXTERNAL: + unfinished_count++; + break; + case CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY: + have_wait_unfinished = TRUE; + break; + case CLIENT_COMMAND_STATE_WAIT_SYNC: + if ((io_loop_find_fd_conditions(current_ioloop, client->fd_out) & IO_WRITE) == 0) + have_wait_unfinished = TRUE; + else { + /* we have an output callback, which will be + called soon and it'll run cmd_sync_delayed(). + FIXME: is this actually wanted? */ + } + break; + case CLIENT_COMMAND_STATE_DONE: + i_unreached(); + } + } + i_assert(!have_wait_unfinished || unfinished_count > 0); +} +static bool client_remove_pending_unambiguity(struct client *client) +{ if (client->input_lock != NULL) { /* there's a command that has locked the input */ struct client_command_context *cmd = client->input_lock; if (cmd->state != CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY) - return; + return FALSE; /* the command is waiting for existing ambiguity causing commands to finish. */ if (client_command_is_ambiguous(cmd)) { /* we could be waiting for existing sync to finish */ if (!cmd_sync_delayed(client)) - return; + return FALSE; if (client_command_is_ambiguous(cmd)) - return; + return FALSE; } cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT; } + return TRUE; +} + +void client_continue_pending_input(struct client *client) +{ + i_assert(!client->handling_input); - client_add_missing_io(client); + /* this function is called at the end of I/O callbacks (and only there). + fix up the command states and verify that they're correct. */ + while (client_remove_pending_unambiguity(client)) { + client_add_missing_io(client); - /* if there's unread data in buffer, handle it. */ - if (i_stream_get_data_size(client->input) > 0 && - !client->disconnected) { - if (client_handle_input(client)) - client_continue_pending_input(client); + /* if there's unread data in buffer, handle it. */ + if (i_stream_get_data_size(client->input) == 0 || + client->disconnected) + break; + if (!client_handle_input(client)) + break; } + client_check_command_hangs(client); } /* Skip incoming data until newline is found, @@ -1021,8 +1068,7 @@ bool client_handle_input(struct client *client) cmd_sync_delayed(client); } else if (client->input_lock->state == CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY) { /* the command may be waiting for previous command to sync. */ - if (cmd_sync_delayed(client)) - client_continue_pending_input(client); + cmd_sync_delayed(client); } return TRUE; } diff --git a/src/imap/imap-client.h b/src/imap/imap-client.h index f1c6cae188..1a7a2e6ea4 100644 --- a/src/imap/imap-client.h +++ b/src/imap/imap-client.h @@ -270,6 +270,8 @@ void client_command_cancel(struct client_command_context **cmd); void client_command_free(struct client_command_context **cmd); bool client_handle_unfinished_cmd(struct client_command_context *cmd); +/* Handle any pending command input. This must be run at the end of all + I/O callbacks after they've (potentially) finished some commands. */ void client_continue_pending_input(struct client *client); void client_add_missing_io(struct client *client); const char *client_stats(struct client *client);