/* Useful for debugging the connection cache */ void Curl_conncache_print(struct conncache *connc) { struct curl_hash_iterator iter; struct curl_llist_element *curr; struct curl_hash_element *he; if(!connc) return; fprintf(stderr, "=Bundle cache=\n"); Curl_hash_start_iterate(connc->hash, &iter); he = Curl_hash_next_element(&iter); while(he) { struct connectbundle *bundle; struct connectdata *conn; bundle = he->ptr; fprintf(stderr, "%s -", he->key); curr = bundle->conn_list->head; while(curr) { conn = curr->ptr; fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse); curr = curr->next; } fprintf(stderr, "\n"); he = Curl_hash_next_element(&iter); } }
/* This function iterates the entire connection cache and calls the function func() with the connection pointer as the first argument and the supplied 'param' argument as the other, Return 0 from func() to continue the loop, return 1 to abort it. */ void Curl_conncache_foreach(struct conncache *connc, void *param, int (*func)(struct connectdata *conn, void *param)) { struct curl_hash_iterator iter; struct curl_llist_element *curr; struct curl_hash_element *he; if(!connc) return; Curl_hash_start_iterate(&connc->hash, &iter); he = Curl_hash_next_element(&iter); while(he) { struct connectbundle *bundle; bundle = he->ptr; he = Curl_hash_next_element(&iter); curr = bundle->conn_list->head; while(curr) { /* Yes, we need to update curr before calling func(), because func() might decide to remove the connection */ struct connectdata *conn = curr->ptr; curr = curr->next; if(1 == func(conn, param)) return; } } }
/* If we have reached the end of the list, find the next one */ if(!iter->current_element) { for(i = iter->slot_index;i < h->slots;i++) { if(h->table[i]->head) { iter->current_element = h->table[i]->head; iter->slot_index = i+1; break; } } } if(iter->current_element) { struct curl_hash_element *he = iter->current_element->ptr; return he; } else { iter->current_element = NULL; return NULL; } } #if 0 /* useful function for debugging hashes and their contents */ void Curl_hash_print(struct curl_hash *h, void (*func)(void *)) { struct curl_hash_iterator iter; struct curl_hash_element *he; int last_index = -1; if(!h) return; fprintf(stderr, "=Hash dump=\n"); Curl_hash_start_iterate(h, &iter); he = Curl_hash_next_element(&iter); while(he) { if(iter.slot_index != last_index) { fprintf(stderr, "index %d:", iter.slot_index); if(last_index >= 0) { fprintf(stderr, "\n"); } last_index = iter.slot_index; } if(func) func(he->ptr); else fprintf(stderr, " [%p]", (void *)he->ptr); he = Curl_hash_next_element(&iter); } fprintf(stderr, "\n"); }
/* * This function finds the connection in the connection cache that has been * unused for the longest time and extracts that from the bundle. * * Returns the pointer to the connection, or NULL if none was found. */ struct connectdata * Curl_conncache_extract_oldest(struct Curl_easy *data) { struct conncache *connc = data->state.conn_cache; struct curl_hash_iterator iter; struct curl_llist_element *curr; struct curl_hash_element *he; timediff_t highscore =- 1; timediff_t score; struct curltime now; struct connectdata *conn_candidate = NULL; struct connectbundle *bundle; struct connectbundle *bundle_candidate = NULL; now = Curl_now(); CONN_LOCK(data); Curl_hash_start_iterate(&connc->hash, &iter); he = Curl_hash_next_element(&iter); while(he) { struct connectdata *conn; bundle = he->ptr; curr = bundle->conn_list.head; while(curr) { conn = curr->ptr; if(!CONN_INUSE(conn)) { /* Set higher score for the age passed since the connection was used */ score = Curl_timediff(now, conn->now); if(score > highscore) { highscore = score; conn_candidate = conn; bundle_candidate = bundle; } } curr = curr->next; } he = Curl_hash_next_element(&iter); } if(conn_candidate) { /* remove it to prevent another thread from nicking it */ bundle_remove_conn(bundle_candidate, conn_candidate); connc->num_conn--; DEBUGF(infof(data, "The cache now contains %zu members\n", connc->num_conn)); conn_candidate->data = data; /* associate! */ } CONN_UNLOCK(data); return conn_candidate; }
static void conncache_remove_bundle(struct conncache *connc, struct connectbundle *bundle) { struct curl_hash_iterator iter; struct curl_hash_element *he; if(!connc) return; Curl_hash_start_iterate(&connc->hash, &iter); he = Curl_hash_next_element(&iter); while(he) { if(he->ptr == bundle) { /* The bundle is destroyed by the hash destructor function, free_bundle_hash_entry() */ Curl_hash_delete(&connc->hash, he->key, he->key_len); return; } he = Curl_hash_next_element(&iter); } }
/* Return the first connection found in the cache. Used when closing all connections */ struct connectdata * Curl_conncache_find_first_connection(struct conncache *connc) { struct curl_hash_iterator iter; struct curl_hash_element *he; struct connectbundle *bundle; Curl_hash_start_iterate(&connc->hash, &iter); he = Curl_hash_next_element(&iter); while(he) { struct curl_llist_element *curr; bundle = he->ptr; curr = bundle->conn_list->head; if(curr) { return curr->ptr; } he = Curl_hash_next_element(&iter); } return NULL; }