From 67c55a26d51149650e91a00b63cf5107989a57e9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 1 Nov 2017 23:37:45 +0100 Subject: share: add support for sharing the connection cache --- lib/conncache.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 8 deletions(-) (limited to 'lib/conncache.c') diff --git a/lib/conncache.c b/lib/conncache.c index c79d22764..f8ef2e88b 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -31,11 +31,21 @@ #include "multiif.h" #include "sendf.h" #include "conncache.h" +#include "share.h" +#include "sigpipe.h" +#include "connect.h" + /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" +#define CONN_LOCK(x) if((x)->share) \ + Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE) +#define CONN_UNLOCK(x) if((x)->share) \ + Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT) + + static void conn_llist_dtor(void *user, void *element) { struct connectdata *data = element; @@ -109,8 +119,23 @@ static void free_bundle_hash_entry(void *freethis) int Curl_conncache_init(struct conncache *connc, int size) { - return Curl_hash_init(&connc->hash, size, Curl_hash_str, - Curl_str_key_compare, free_bundle_hash_entry); + int rc; + + /* allocate a new easy handle to use when closing cached connections */ + connc->closure_handle = curl_easy_init(); + if(!connc->closure_handle) + return 1; /* bad */ + + rc = Curl_hash_init(&connc->hash, size, Curl_hash_str, + Curl_str_key_compare, free_bundle_hash_entry); + if(rc) { + Curl_close(connc->closure_handle); + connc->closure_handle = NULL; + } + else + connc->closure_handle->state.conn_cache = connc; + + return rc; } void Curl_conncache_destroy(struct conncache *connc) @@ -149,7 +174,9 @@ struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn, if(connc) { char key[128]; hashkey(conn, key, sizeof(key)); + CONN_LOCK(conn->data); bundle = Curl_hash_pick(&connc->hash, key, strlen(key)); + CONN_UNLOCK(conn->data); } return bundle; @@ -206,7 +233,9 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc, return result; hashkey(conn, key, sizeof(key)); + CONN_LOCK(data); rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle); + CONN_UNLOCK(data); if(!rc) { bundle_destroy(new_bundle); @@ -215,12 +244,15 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc, bundle = new_bundle; } + CONN_LOCK(data); result = bundle_add_conn(bundle, conn); if(result) { if(new_bundle) conncache_remove_bundle(data->state.conn_cache, new_bundle); + CONN_UNLOCK(data); return result; } + CONN_UNLOCK(data); conn->connection_id = connc->next_connection_id++; connc->num_connections++; @@ -240,11 +272,11 @@ void Curl_conncache_remove_conn(struct conncache *connc, /* The bundle pointer can be NULL, since this function can be called due to a failed connection attempt, before being added to a bundle */ if(bundle) { + CONN_LOCK(conn->data); bundle_remove_conn(bundle, conn); - if(bundle->num_connections == 0) { + if(bundle->num_connections == 0) conncache_remove_bundle(connc, bundle); - } - + CONN_UNLOCK(conn->data); if(connc) { connc->num_connections--; @@ -261,7 +293,8 @@ void Curl_conncache_remove_conn(struct conncache *connc, Return 0 from func() to continue the loop, return 1 to abort it. */ -void Curl_conncache_foreach(struct conncache *connc, +void Curl_conncache_foreach(struct Curl_easy *data, + struct conncache *connc, void *param, int (*func)(struct connectdata *conn, void *param)) { @@ -272,6 +305,7 @@ void Curl_conncache_foreach(struct conncache *connc, if(!connc) return; + CONN_LOCK(data); Curl_hash_start_iterate(&connc->hash, &iter); he = Curl_hash_next_element(&iter); @@ -288,14 +322,21 @@ void Curl_conncache_foreach(struct conncache *connc, struct connectdata *conn = curr->ptr; curr = curr->next; - if(1 == func(conn, param)) + if(1 == func(conn, param)) { + CONN_UNLOCK(data); return; + } } } + CONN_UNLOCK(data); } /* Return the first connection found in the cache. Used when closing all - connections */ + connections. + + NOTE: no locking is done here as this is presumably only done when cleaning + up a cache! +*/ struct connectdata * Curl_conncache_find_first_connection(struct conncache *connc) { @@ -321,6 +362,90 @@ Curl_conncache_find_first_connection(struct conncache *connc) return NULL; } +/* + * This function finds the connection in the connection + * cache that has been unused for the longest time. + * + * Returns the pointer to the oldest idle connection, or NULL if none was + * found. + */ +struct connectdata * +Curl_conncache_oldest_idle(struct Curl_easy *data) +{ + struct conncache *bc = 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; + + now = Curl_now(); + + CONN_LOCK(data); + Curl_hash_start_iterate(&bc->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) { + /* 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; + } + } + curr = curr->next; + } + + he = Curl_hash_next_element(&iter); + } + CONN_UNLOCK(data); + + return conn_candidate; +} + +void Curl_conncache_close_all_connections(struct conncache *connc) +{ + struct connectdata *conn; + + conn = Curl_conncache_find_first_connection(connc); + while(conn) { + SIGPIPE_VARIABLE(pipe_st); + conn->data = connc->closure_handle; + + sigpipe_ignore(conn->data, &pipe_st); + conn->data->easy_conn = NULL; /* clear the easy handle's connection + pointer */ + /* This will remove the connection from the cache */ + connclose(conn, "kill all"); + (void)Curl_disconnect(conn, FALSE); + sigpipe_restore(&pipe_st); + + conn = Curl_conncache_find_first_connection(connc); + } + + if(connc->closure_handle) { + SIGPIPE_VARIABLE(pipe_st); + sigpipe_ignore(connc->closure_handle, &pipe_st); + + Curl_hostcache_clean(connc->closure_handle, + connc->closure_handle->dns.hostcache); + Curl_close(connc->closure_handle); + sigpipe_restore(&pipe_st); + } +} #if 0 /* Useful for debugging the connection cache */ -- cgit v1.2.3