summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Klauer <Alexander.Klauer@itwm.fraunhofer.de>2013-04-08 09:48:43 (GMT)
committerDaniel Stenberg <daniel@haxx.se>2013-04-08 20:16:48 (GMT)
commit54faf09da68691a88dbf1936f5969b53af364261 (patch)
tree15b181c46c5fe6ea060b737d070e22b8e388e419
parent127d4cb35798697b8d9a87648dd9af66d4ed9dec (diff)
downloadc-ares-54faf09da68691a88dbf1936f5969b53af364261.zip
c-ares-54faf09da68691a88dbf1936f5969b53af364261.tar.gz
c-ares-54faf09da68691a88dbf1936f5969b53af364261.tar.bz2
ares_cancel(): cancel requests safely
An invocation of ares_cancel() walks through the request list, calling the callbacks of all pending requests on a channel. Previously, if such a callback added a new request to the channel, the request list might not end up empty, causing an abort by assertion failure. The present commit ensures that precisely all requests present upon entry of ares_cancel() are cancelled, and that adding new requests through callbacks is safe.
-rw-r--r--ares_cancel.c40
1 files changed, 20 insertions, 20 deletions
diff --git a/ares_cancel.c b/ares_cancel.c
index e5bb050..465cc9e 100644
--- a/ares_cancel.c
+++ b/ares_cancel.c
@@ -26,33 +26,33 @@
void ares_cancel(ares_channel channel)
{
struct query *query;
+ struct list_node list_head_copy;
struct list_node* list_head;
struct list_node* list_node;
int i;
- list_head = &(channel->all_queries);
- for (list_node = list_head->next; list_node != list_head; )
+ if (!ares__is_list_empty(&(channel->all_queries)))
{
- query = list_node->data;
- list_node = list_node->next; /* since we're deleting the query */
- query->callback(query->arg, ARES_ECANCELLED, 0, NULL, 0);
- ares__free_query(query);
- }
-#ifndef NDEBUG
- /* Freeing the query should remove it from all the lists in which it sits,
- * so all query lists should be empty now.
- */
- assert(ares__is_list_empty(&(channel->all_queries)));
- for (i = 0; i < ARES_QID_TABLE_SIZE; i++)
- {
- assert(ares__is_list_empty(&(channel->queries_by_qid[i])));
- }
- for (i = 0; i < ARES_TIMEOUT_TABLE_SIZE; i++)
+ /* Swap list heads, so that only those queries which were present on entry
+ * into this function are cancelled. New queries added by callbacks of
+ * queries being cancelled will not be cancelled themselves.
+ */
+ list_head = &(channel->all_queries);
+ list_head_copy.prev = list_head->prev;
+ list_head_copy.next = list_head->next;
+ list_head_copy.prev->next = &list_head_copy;
+ list_head_copy.next->prev = &list_head_copy;
+ list_head->prev = list_head;
+ list_head->next = list_head;
+ for (list_node = list_head_copy.next; list_node != &list_head_copy; )
{
- assert(ares__is_list_empty(&(channel->queries_by_timeout[i])));
+ query = list_node->data;
+ list_node = list_node->next; /* since we're deleting the query */
+ query->callback(query->arg, ARES_ECANCELLED, 0, NULL, 0);
+ ares__free_query(query);
}
-#endif
- if (!(channel->flags & ARES_FLAG_STAYOPEN))
+ }
+ if (!(channel->flags & ARES_FLAG_STAYOPEN) && ares__is_list_empty(&(channel->all_queries)))
{
if (channel->servers)
{