mirror of
https://github.com/moparisthebest/curl
synced 2024-12-21 15:48:49 -05:00
conncache: fix several lock issues
If the lock is released before the dealings with the bundle is over, it may have changed by another thread in the mean time. Fixes #2132 Fixes #2151 Closes #2139
This commit is contained in:
parent
85f0133ea1
commit
07cb27c98e
222
lib/conncache.c
222
lib/conncache.c
@ -40,11 +40,26 @@
|
|||||||
#include "curl_memory.h"
|
#include "curl_memory.h"
|
||||||
#include "memdebug.h"
|
#include "memdebug.h"
|
||||||
|
|
||||||
|
#ifdef CURLDEBUG
|
||||||
|
/* the debug versions of these macros make extra certain that the lock is
|
||||||
|
never doubly locked or unlocked */
|
||||||
|
#define CONN_LOCK(x) if((x)->share) { \
|
||||||
|
Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE); \
|
||||||
|
DEBUGASSERT(!(x)->state.conncache_lock); \
|
||||||
|
(x)->state.conncache_lock = TRUE; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CONN_UNLOCK(x) if((x)->share) { \
|
||||||
|
DEBUGASSERT((x)->state.conncache_lock); \
|
||||||
|
(x)->state.conncache_lock = FALSE; \
|
||||||
|
Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT); \
|
||||||
|
}
|
||||||
|
#else
|
||||||
#define CONN_LOCK(x) if((x)->share) \
|
#define CONN_LOCK(x) if((x)->share) \
|
||||||
Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE)
|
Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE)
|
||||||
#define CONN_UNLOCK(x) if((x)->share) \
|
#define CONN_UNLOCK(x) if((x)->share) \
|
||||||
Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT)
|
Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT)
|
||||||
|
#endif
|
||||||
|
|
||||||
static void conn_llist_dtor(void *user, void *element)
|
static void conn_llist_dtor(void *user, void *element)
|
||||||
{
|
{
|
||||||
@ -165,18 +180,48 @@ static void hashkey(struct connectdata *conn, char *buf,
|
|||||||
snprintf(buf, len, "%ld%s", conn->port, hostname);
|
snprintf(buf, len, "%ld%s", conn->port, hostname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Curl_conncache_unlock(struct connectdata *conn)
|
||||||
|
{
|
||||||
|
CONN_UNLOCK(conn->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns number of connections currently held in the connection cache.
|
||||||
|
Locks/unlocks the cache itself!
|
||||||
|
*/
|
||||||
|
size_t Curl_conncache_size(struct Curl_easy *data)
|
||||||
|
{
|
||||||
|
size_t num;
|
||||||
|
CONN_LOCK(data);
|
||||||
|
num = data->state.conn_cache->num_conn;
|
||||||
|
CONN_UNLOCK(data);
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns number of connections currently held in the connections's bundle
|
||||||
|
Locks/unlocks the cache itself!
|
||||||
|
*/
|
||||||
|
size_t Curl_conncache_bundle_size(struct connectdata *conn)
|
||||||
|
{
|
||||||
|
size_t num;
|
||||||
|
CONN_LOCK(conn->data);
|
||||||
|
num = conn->bundle->num_connections;
|
||||||
|
CONN_UNLOCK(conn->data);
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
/* Look up the bundle with all the connections to the same host this
|
/* Look up the bundle with all the connections to the same host this
|
||||||
connectdata struct is setup to use. */
|
connectdata struct is setup to use.
|
||||||
|
|
||||||
|
**NOTE**: When it returns, it holds the connection cache lock! */
|
||||||
struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
|
struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
|
||||||
struct conncache *connc)
|
struct conncache *connc)
|
||||||
{
|
{
|
||||||
struct connectbundle *bundle = NULL;
|
struct connectbundle *bundle = NULL;
|
||||||
|
CONN_LOCK(conn->data);
|
||||||
if(connc) {
|
if(connc) {
|
||||||
char key[128];
|
char key[128];
|
||||||
hashkey(conn, key, sizeof(key));
|
hashkey(conn, key, sizeof(key));
|
||||||
CONN_LOCK(conn->data);
|
|
||||||
bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
|
bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
|
||||||
CONN_UNLOCK(conn->data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bundle;
|
return bundle;
|
||||||
@ -223,77 +268,89 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc,
|
|||||||
struct connectbundle *new_bundle = NULL;
|
struct connectbundle *new_bundle = NULL;
|
||||||
struct Curl_easy *data = conn->data;
|
struct Curl_easy *data = conn->data;
|
||||||
|
|
||||||
|
/* *find_bundle() locks the connection cache */
|
||||||
bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
|
bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
|
||||||
if(!bundle) {
|
if(!bundle) {
|
||||||
int rc;
|
int rc;
|
||||||
char key[128];
|
char key[128];
|
||||||
|
|
||||||
result = bundle_create(data, &new_bundle);
|
result = bundle_create(data, &new_bundle);
|
||||||
if(result)
|
if(result) {
|
||||||
return result;
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
hashkey(conn, key, sizeof(key));
|
hashkey(conn, key, sizeof(key));
|
||||||
CONN_LOCK(data);
|
|
||||||
rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle);
|
rc = conncache_add_bundle(data->state.conn_cache, key, new_bundle);
|
||||||
CONN_UNLOCK(data);
|
|
||||||
|
|
||||||
if(!rc) {
|
if(!rc) {
|
||||||
bundle_destroy(new_bundle);
|
bundle_destroy(new_bundle);
|
||||||
return CURLE_OUT_OF_MEMORY;
|
result = CURLE_OUT_OF_MEMORY;
|
||||||
|
goto unlock;
|
||||||
}
|
}
|
||||||
bundle = new_bundle;
|
bundle = new_bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
CONN_LOCK(data);
|
|
||||||
result = bundle_add_conn(bundle, conn);
|
result = bundle_add_conn(bundle, conn);
|
||||||
if(result) {
|
if(result) {
|
||||||
if(new_bundle)
|
if(new_bundle)
|
||||||
conncache_remove_bundle(data->state.conn_cache, new_bundle);
|
conncache_remove_bundle(data->state.conn_cache, new_bundle);
|
||||||
CONN_UNLOCK(data);
|
goto unlock;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
CONN_UNLOCK(data);
|
|
||||||
|
|
||||||
conn->connection_id = connc->next_connection_id++;
|
conn->connection_id = connc->next_connection_id++;
|
||||||
connc->num_connections++;
|
connc->num_conn++;
|
||||||
|
|
||||||
DEBUGF(infof(conn->data, "Added connection %ld. "
|
DEBUGF(infof(conn->data, "Added connection %ld. "
|
||||||
"The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n",
|
"The cache now contains %" CURL_FORMAT_CURL_OFF_TU " members\n",
|
||||||
conn->connection_id, (curl_off_t) connc->num_connections));
|
conn->connection_id, (curl_off_t) connc->num_conn));
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
CONN_UNLOCK(data);
|
||||||
|
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Curl_conncache_remove_conn(struct conncache *connc,
|
void Curl_conncache_remove_conn(struct connectdata *conn, bool lock)
|
||||||
struct connectdata *conn)
|
|
||||||
{
|
{
|
||||||
|
struct Curl_easy *data = conn->data;
|
||||||
struct connectbundle *bundle = conn->bundle;
|
struct connectbundle *bundle = conn->bundle;
|
||||||
|
struct conncache *connc = data->state.conn_cache;
|
||||||
|
|
||||||
/* The bundle pointer can be NULL, since this function can be called
|
/* The bundle pointer can be NULL, since this function can be called
|
||||||
due to a failed connection attempt, before being added to a bundle */
|
due to a failed connection attempt, before being added to a bundle */
|
||||||
if(bundle) {
|
if(bundle) {
|
||||||
CONN_LOCK(conn->data);
|
if(lock) {
|
||||||
|
CONN_LOCK(conn->data);
|
||||||
|
}
|
||||||
bundle_remove_conn(bundle, conn);
|
bundle_remove_conn(bundle, conn);
|
||||||
if(bundle->num_connections == 0)
|
if(bundle->num_connections == 0)
|
||||||
conncache_remove_bundle(connc, bundle);
|
conncache_remove_bundle(connc, bundle);
|
||||||
CONN_UNLOCK(conn->data);
|
conn->bundle = NULL; /* removed from it */
|
||||||
if(connc) {
|
if(connc) {
|
||||||
connc->num_connections--;
|
connc->num_conn--;
|
||||||
|
|
||||||
DEBUGF(infof(conn->data, "The cache now contains %"
|
DEBUGF(infof(conn->data, "The cache now contains %"
|
||||||
CURL_FORMAT_CURL_OFF_TU " members\n",
|
CURL_FORMAT_CURL_OFF_TU " members\n",
|
||||||
(curl_off_t) connc->num_connections));
|
(curl_off_t) connc->num_conn));
|
||||||
|
}
|
||||||
|
if(lock) {
|
||||||
|
CONN_UNLOCK(conn->data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function iterates the entire connection cache and calls the
|
/* This function iterates the entire connection cache and calls the function
|
||||||
function func() with the connection pointer as the first argument
|
func() with the connection pointer as the first argument and the supplied
|
||||||
and the supplied 'param' argument as the other,
|
'param' argument as the other.
|
||||||
|
|
||||||
|
The conncache lock is still held when the callback is called. It needs it,
|
||||||
|
so that it can safely continue traversing the lists once the callback
|
||||||
|
returns.
|
||||||
|
|
||||||
|
Returns 1 if the loop was aborted due to the callback's return code.
|
||||||
|
|
||||||
Return 0 from func() to continue the loop, return 1 to abort it.
|
Return 0 from func() to continue the loop, return 1 to abort it.
|
||||||
*/
|
*/
|
||||||
void Curl_conncache_foreach(struct Curl_easy *data,
|
bool Curl_conncache_foreach(struct Curl_easy *data,
|
||||||
struct conncache *connc,
|
struct conncache *connc,
|
||||||
void *param,
|
void *param,
|
||||||
int (*func)(struct connectdata *conn, void *param))
|
int (*func)(struct connectdata *conn, void *param))
|
||||||
@ -303,7 +360,7 @@ void Curl_conncache_foreach(struct Curl_easy *data,
|
|||||||
struct curl_hash_element *he;
|
struct curl_hash_element *he;
|
||||||
|
|
||||||
if(!connc)
|
if(!connc)
|
||||||
return;
|
return FALSE;
|
||||||
|
|
||||||
CONN_LOCK(data);
|
CONN_LOCK(data);
|
||||||
Curl_hash_start_iterate(&connc->hash, &iter);
|
Curl_hash_start_iterate(&connc->hash, &iter);
|
||||||
@ -324,11 +381,12 @@ void Curl_conncache_foreach(struct Curl_easy *data,
|
|||||||
|
|
||||||
if(1 == func(conn, param)) {
|
if(1 == func(conn, param)) {
|
||||||
CONN_UNLOCK(data);
|
CONN_UNLOCK(data);
|
||||||
return;
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CONN_UNLOCK(data);
|
CONN_UNLOCK(data);
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the first connection found in the cache. Used when closing all
|
/* Return the first connection found in the cache. Used when closing all
|
||||||
@ -363,16 +421,104 @@ Curl_conncache_find_first_connection(struct conncache *connc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function finds the connection in the connection
|
* Give ownership of a connection back to the connection cache. Might
|
||||||
* cache that has been unused for the longest time.
|
* disconnect the oldest existing in there to make space.
|
||||||
|
*
|
||||||
|
* Return TRUE if stored, FALSE if closed.
|
||||||
|
*/
|
||||||
|
bool Curl_conncache_return_conn(struct connectdata *conn)
|
||||||
|
{
|
||||||
|
struct Curl_easy *data = conn->data;
|
||||||
|
|
||||||
|
/* data->multi->maxconnects can be negative, deal with it. */
|
||||||
|
size_t maxconnects =
|
||||||
|
(data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
|
||||||
|
data->multi->maxconnects;
|
||||||
|
struct connectdata *conn_candidate = NULL;
|
||||||
|
|
||||||
|
if(maxconnects > 0 &&
|
||||||
|
Curl_conncache_size(data) > maxconnects) {
|
||||||
|
infof(data, "Connection cache is full, closing the oldest one.\n");
|
||||||
|
|
||||||
|
conn_candidate = Curl_conncache_extract_oldest(data);
|
||||||
|
|
||||||
|
if(conn_candidate) {
|
||||||
|
/* Set the connection's owner correctly */
|
||||||
|
conn_candidate->data = data;
|
||||||
|
|
||||||
|
/* the winner gets the honour of being disconnected */
|
||||||
|
(void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CONN_LOCK(data);
|
||||||
|
conn->inuse = FALSE; /* Mark the connection unused */
|
||||||
|
CONN_UNLOCK(data);
|
||||||
|
|
||||||
|
return (conn_candidate == conn) ? FALSE : TRUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function finds the connection in the connection bundle that has been
|
||||||
|
* unused for the longest time.
|
||||||
|
*
|
||||||
|
* Does not lock the connection cache!
|
||||||
*
|
*
|
||||||
* Returns the pointer to the oldest idle connection, or NULL if none was
|
* Returns the pointer to the oldest idle connection, or NULL if none was
|
||||||
* found.
|
* found.
|
||||||
*/
|
*/
|
||||||
struct connectdata *
|
struct connectdata *
|
||||||
Curl_conncache_oldest_idle(struct Curl_easy *data)
|
Curl_conncache_extract_bundle(struct Curl_easy *data,
|
||||||
|
struct connectbundle *bundle)
|
||||||
{
|
{
|
||||||
struct conncache *bc = data->state.conn_cache;
|
struct curl_llist_element *curr;
|
||||||
|
timediff_t highscore = -1;
|
||||||
|
timediff_t score;
|
||||||
|
struct curltime now;
|
||||||
|
struct connectdata *conn_candidate = NULL;
|
||||||
|
struct connectdata *conn;
|
||||||
|
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
now = Curl_now();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if(conn_candidate) {
|
||||||
|
/* remove it to prevent another thread from nicking it */
|
||||||
|
bundle_remove_conn(bundle, conn_candidate);
|
||||||
|
data->state.conn_cache->num_conn--;
|
||||||
|
DEBUGF(infof(data, "The cache now contains %"
|
||||||
|
CURL_FORMAT_CURL_OFF_TU " members\n",
|
||||||
|
(curl_off_t) data->state.conn_cache->num_conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn_candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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_hash_iterator iter;
|
||||||
struct curl_llist_element *curr;
|
struct curl_llist_element *curr;
|
||||||
struct curl_hash_element *he;
|
struct curl_hash_element *he;
|
||||||
@ -381,11 +527,12 @@ Curl_conncache_oldest_idle(struct Curl_easy *data)
|
|||||||
struct curltime now;
|
struct curltime now;
|
||||||
struct connectdata *conn_candidate = NULL;
|
struct connectdata *conn_candidate = NULL;
|
||||||
struct connectbundle *bundle;
|
struct connectbundle *bundle;
|
||||||
|
struct connectbundle *bundle_candidate = NULL;
|
||||||
|
|
||||||
now = Curl_now();
|
now = Curl_now();
|
||||||
|
|
||||||
CONN_LOCK(data);
|
CONN_LOCK(data);
|
||||||
Curl_hash_start_iterate(&bc->hash, &iter);
|
Curl_hash_start_iterate(&connc->hash, &iter);
|
||||||
|
|
||||||
he = Curl_hash_next_element(&iter);
|
he = Curl_hash_next_element(&iter);
|
||||||
while(he) {
|
while(he) {
|
||||||
@ -404,6 +551,7 @@ Curl_conncache_oldest_idle(struct Curl_easy *data)
|
|||||||
if(score > highscore) {
|
if(score > highscore) {
|
||||||
highscore = score;
|
highscore = score;
|
||||||
conn_candidate = conn;
|
conn_candidate = conn;
|
||||||
|
bundle_candidate = bundle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
curr = curr->next;
|
curr = curr->next;
|
||||||
@ -411,6 +559,14 @@ Curl_conncache_oldest_idle(struct Curl_easy *data)
|
|||||||
|
|
||||||
he = Curl_hash_next_element(&iter);
|
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 %"
|
||||||
|
CURL_FORMAT_CURL_OFF_TU " members\n",
|
||||||
|
(curl_off_t) connc->num_conn));
|
||||||
|
}
|
||||||
CONN_UNLOCK(data);
|
CONN_UNLOCK(data);
|
||||||
|
|
||||||
return conn_candidate;
|
return conn_candidate;
|
||||||
|
@ -23,9 +23,15 @@
|
|||||||
*
|
*
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All accesses to struct fields and changing of data in the connection cache
|
||||||
|
* and connectbundles must be done with the conncache LOCKED. The cache might
|
||||||
|
* be shared.
|
||||||
|
*/
|
||||||
|
|
||||||
struct conncache {
|
struct conncache {
|
||||||
struct curl_hash hash;
|
struct curl_hash hash;
|
||||||
size_t num_connections;
|
size_t num_conn;
|
||||||
long next_connection_id;
|
long next_connection_id;
|
||||||
struct curltime last_cleanup;
|
struct curltime last_cleanup;
|
||||||
/* handle used for closing cached connections */
|
/* handle used for closing cached connections */
|
||||||
@ -50,14 +56,17 @@ void Curl_conncache_destroy(struct conncache *connc);
|
|||||||
/* return the correct bundle, to a host or a proxy */
|
/* return the correct bundle, to a host or a proxy */
|
||||||
struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
|
struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
|
||||||
struct conncache *connc);
|
struct conncache *connc);
|
||||||
|
void Curl_conncache_unlock(struct connectdata *conn);
|
||||||
|
/* returns number of connections currently held in the connection cache */
|
||||||
|
size_t Curl_conncache_size(struct Curl_easy *data);
|
||||||
|
size_t Curl_conncache_bundle_size(struct connectdata *conn);
|
||||||
|
|
||||||
|
bool Curl_conncache_return_conn(struct connectdata *conn);
|
||||||
CURLcode Curl_conncache_add_conn(struct conncache *connc,
|
CURLcode Curl_conncache_add_conn(struct conncache *connc,
|
||||||
struct connectdata *conn);
|
struct connectdata *conn);
|
||||||
|
void Curl_conncache_remove_conn(struct connectdata *conn,
|
||||||
void Curl_conncache_remove_conn(struct conncache *connc,
|
bool lock);
|
||||||
struct connectdata *conn);
|
bool Curl_conncache_foreach(struct Curl_easy *data,
|
||||||
|
|
||||||
void Curl_conncache_foreach(struct Curl_easy *data,
|
|
||||||
struct conncache *connc,
|
struct conncache *connc,
|
||||||
void *param,
|
void *param,
|
||||||
int (*func)(struct connectdata *conn,
|
int (*func)(struct connectdata *conn,
|
||||||
@ -67,7 +76,10 @@ struct connectdata *
|
|||||||
Curl_conncache_find_first_connection(struct conncache *connc);
|
Curl_conncache_find_first_connection(struct conncache *connc);
|
||||||
|
|
||||||
struct connectdata *
|
struct connectdata *
|
||||||
Curl_conncache_oldest_idle(struct Curl_easy *data);
|
Curl_conncache_extract_bundle(struct Curl_easy *data,
|
||||||
|
struct connectbundle *bundle);
|
||||||
|
struct connectdata *
|
||||||
|
Curl_conncache_extract_oldest(struct Curl_easy *data);
|
||||||
void Curl_conncache_close_all_connections(struct conncache *connc);
|
void Curl_conncache_close_all_connections(struct conncache *connc);
|
||||||
void Curl_conncache_print(struct conncache *connc);
|
void Curl_conncache_print(struct conncache *connc);
|
||||||
|
|
||||||
|
56
lib/multi.c
56
lib/multi.c
@ -479,38 +479,6 @@ static void debug_print_sock_hash(void *p)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Mark the connection as 'idle', or close it if the cache is full.
|
|
||||||
Returns TRUE if the connection is kept, or FALSE if it was closed. */
|
|
||||||
static bool
|
|
||||||
ConnectionDone(struct Curl_easy *data, struct connectdata *conn)
|
|
||||||
{
|
|
||||||
/* data->multi->maxconnects can be negative, deal with it. */
|
|
||||||
size_t maxconnects =
|
|
||||||
(data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
|
|
||||||
data->multi->maxconnects;
|
|
||||||
struct connectdata *conn_candidate = NULL;
|
|
||||||
|
|
||||||
/* Mark the current connection as 'unused' */
|
|
||||||
conn->inuse = FALSE;
|
|
||||||
|
|
||||||
if(maxconnects > 0 &&
|
|
||||||
data->state.conn_cache->num_connections > maxconnects) {
|
|
||||||
infof(data, "Connection cache is full, closing the oldest one.\n");
|
|
||||||
|
|
||||||
conn_candidate = Curl_conncache_oldest_idle(data);
|
|
||||||
|
|
||||||
if(conn_candidate) {
|
|
||||||
/* Set the connection's owner correctly */
|
|
||||||
conn_candidate->data = data;
|
|
||||||
|
|
||||||
/* the winner gets the honour of being disconnected */
|
|
||||||
(void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (conn_candidate == conn) ? FALSE : TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static CURLcode multi_done(struct connectdata **connp,
|
static CURLcode multi_done(struct connectdata **connp,
|
||||||
CURLcode status, /* an error if this is called
|
CURLcode status, /* an error if this is called
|
||||||
after an error was detected */
|
after an error was detected */
|
||||||
@ -621,17 +589,21 @@ static CURLcode multi_done(struct connectdata **connp,
|
|||||||
result = res2;
|
result = res2;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
char buffer[256];
|
||||||
|
/* create string before returning the connection */
|
||||||
|
snprintf(buffer, sizeof(buffer),
|
||||||
|
"Connection #%ld to host %s left intact",
|
||||||
|
conn->connection_id,
|
||||||
|
conn->bits.socksproxy ? conn->socks_proxy.host.dispname :
|
||||||
|
conn->bits.httpproxy ? conn->http_proxy.host.dispname :
|
||||||
|
conn->bits.conn_to_host ? conn->conn_to_host.dispname :
|
||||||
|
conn->host.dispname);
|
||||||
|
|
||||||
/* the connection is no longer in use */
|
/* the connection is no longer in use */
|
||||||
if(ConnectionDone(data, conn)) {
|
if(Curl_conncache_return_conn(conn)) {
|
||||||
/* remember the most recently used connection */
|
/* remember the most recently used connection */
|
||||||
data->state.lastconnect = conn;
|
data->state.lastconnect = conn;
|
||||||
|
infof(data, "%s\n", buffer);
|
||||||
infof(data, "Connection #%ld to host %s left intact\n",
|
|
||||||
conn->connection_id,
|
|
||||||
conn->bits.socksproxy ? conn->socks_proxy.host.dispname :
|
|
||||||
conn->bits.httpproxy ? conn->http_proxy.host.dispname :
|
|
||||||
conn->bits.conn_to_host ? conn->conn_to_host.dispname :
|
|
||||||
conn->host.dispname);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
data->state.lastconnect = NULL;
|
data->state.lastconnect = NULL;
|
||||||
@ -1357,16 +1329,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(data->easy_conn && data->mstate > CURLM_STATE_CONNECT &&
|
if(data->easy_conn && data->mstate > CURLM_STATE_CONNECT &&
|
||||||
data->mstate < CURLM_STATE_COMPLETED)
|
data->mstate < CURLM_STATE_COMPLETED) {
|
||||||
/* Make sure we set the connection's current owner */
|
/* Make sure we set the connection's current owner */
|
||||||
data->easy_conn->data = data;
|
data->easy_conn->data = data;
|
||||||
|
}
|
||||||
|
|
||||||
if(data->easy_conn &&
|
if(data->easy_conn &&
|
||||||
(data->mstate >= CURLM_STATE_CONNECT) &&
|
(data->mstate >= CURLM_STATE_CONNECT) &&
|
||||||
(data->mstate < CURLM_STATE_COMPLETED)) {
|
(data->mstate < CURLM_STATE_COMPLETED)) {
|
||||||
/* we need to wait for the connect state as only then is the start time
|
/* we need to wait for the connect state as only then is the start time
|
||||||
stored, but we must not check already completed handles */
|
stored, but we must not check already completed handles */
|
||||||
|
|
||||||
timeout_ms = Curl_timeleft(data, &now,
|
timeout_ms = Curl_timeleft(data, &now,
|
||||||
(data->mstate <= CURLM_STATE_WAITDO)?
|
(data->mstate <= CURLM_STATE_WAITDO)?
|
||||||
TRUE:FALSE);
|
TRUE:FALSE);
|
||||||
|
168
lib/url.c
168
lib/url.c
@ -127,10 +127,6 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
|
|||||||
#include "curl_memory.h"
|
#include "curl_memory.h"
|
||||||
#include "memdebug.h"
|
#include "memdebug.h"
|
||||||
|
|
||||||
/* Local static prototypes */
|
|
||||||
static struct connectdata *
|
|
||||||
find_oldest_idle_connection_in_bundle(struct Curl_easy *data,
|
|
||||||
struct connectbundle *bundle);
|
|
||||||
static void conn_free(struct connectdata *conn);
|
static void conn_free(struct connectdata *conn);
|
||||||
static void free_fixed_hostname(struct hostname *host);
|
static void free_fixed_hostname(struct hostname *host);
|
||||||
static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke);
|
static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke);
|
||||||
@ -722,7 +718,6 @@ static void conn_free(struct connectdata *conn)
|
|||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
Curl_safefree(conn->ssl_extra);
|
Curl_safefree(conn->ssl_extra);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
free(conn); /* free all the connection oriented data */
|
free(conn); /* free all the connection oriented data */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -777,7 +772,7 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection)
|
|||||||
|
|
||||||
/* unlink ourselves! */
|
/* unlink ourselves! */
|
||||||
infof(data, "Closing connection %ld\n", conn->connection_id);
|
infof(data, "Closing connection %ld\n", conn->connection_id);
|
||||||
Curl_conncache_remove_conn(data->state.conn_cache, conn);
|
Curl_conncache_remove_conn(conn, TRUE);
|
||||||
|
|
||||||
free_fixed_hostname(&conn->host);
|
free_fixed_hostname(&conn->host);
|
||||||
free_fixed_hostname(&conn->conn_to_host);
|
free_fixed_hostname(&conn->conn_to_host);
|
||||||
@ -943,56 +938,17 @@ proxy_info_matches(const struct proxy_info* data,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function finds the connection in the connection
|
* This function checks if the given connection is dead and extracts it from
|
||||||
* bundle that has been unused for the longest time.
|
* the connection cache if so.
|
||||||
*
|
*
|
||||||
* Returns the pointer to the oldest idle connection, or NULL if none was
|
* When this is called as a Curl_conncache_foreach() callback, the connection
|
||||||
* found.
|
* cache lock is held!
|
||||||
*/
|
|
||||||
static struct connectdata *
|
|
||||||
find_oldest_idle_connection_in_bundle(struct Curl_easy *data,
|
|
||||||
struct connectbundle *bundle)
|
|
||||||
{
|
|
||||||
struct curl_llist_element *curr;
|
|
||||||
timediff_t highscore = -1;
|
|
||||||
timediff_t score;
|
|
||||||
struct curltime now;
|
|
||||||
struct connectdata *conn_candidate = NULL;
|
|
||||||
struct connectdata *conn;
|
|
||||||
|
|
||||||
(void)data;
|
|
||||||
|
|
||||||
now = Curl_now();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return conn_candidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This function checks if given connection is dead and disconnects if so.
|
|
||||||
* (That also removes it from the connection cache.)
|
|
||||||
*
|
*
|
||||||
* Returns TRUE if the connection actually was dead and disconnected.
|
* Returns TRUE if the connection was dead and extracted.
|
||||||
*/
|
*/
|
||||||
static bool disconnect_if_dead(struct connectdata *conn,
|
static bool extract_if_dead(struct connectdata *conn,
|
||||||
struct Curl_easy *data)
|
struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
size_t pipeLen = conn->send_pipe.size + conn->recv_pipe.size;
|
size_t pipeLen = conn->send_pipe.size + conn->recv_pipe.size;
|
||||||
if(!pipeLen && !conn->inuse) {
|
if(!pipeLen && !conn->inuse) {
|
||||||
@ -1017,25 +973,30 @@ static bool disconnect_if_dead(struct connectdata *conn,
|
|||||||
if(dead) {
|
if(dead) {
|
||||||
conn->data = data;
|
conn->data = data;
|
||||||
infof(data, "Connection %ld seems to be dead!\n", conn->connection_id);
|
infof(data, "Connection %ld seems to be dead!\n", conn->connection_id);
|
||||||
|
Curl_conncache_remove_conn(conn, FALSE);
|
||||||
/* disconnect resources */
|
|
||||||
Curl_disconnect(conn, /* dead_connection */TRUE);
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct prunedead {
|
||||||
|
struct Curl_easy *data;
|
||||||
|
struct connectdata *extracted;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wrapper to use disconnect_if_dead() function in Curl_conncache_foreach()
|
* Wrapper to use extract_if_dead() function in Curl_conncache_foreach()
|
||||||
*
|
*
|
||||||
* Returns always 0.
|
|
||||||
*/
|
*/
|
||||||
static int call_disconnect_if_dead(struct connectdata *conn,
|
static int call_extract_if_dead(struct connectdata *conn, void *param)
|
||||||
void *param)
|
|
||||||
{
|
{
|
||||||
struct Curl_easy* data = (struct Curl_easy*)param;
|
struct prunedead *p = (struct prunedead *)param;
|
||||||
disconnect_if_dead(conn, data);
|
if(extract_if_dead(conn, p->data)) {
|
||||||
|
/* stop the iteration here, pass back the connection that was extracted */
|
||||||
|
p->extracted = conn;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
return 0; /* continue iteration */
|
return 0; /* continue iteration */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1050,8 +1011,14 @@ static void prune_dead_connections(struct Curl_easy *data)
|
|||||||
time_t elapsed = Curl_timediff(now, data->state.conn_cache->last_cleanup);
|
time_t elapsed = Curl_timediff(now, data->state.conn_cache->last_cleanup);
|
||||||
|
|
||||||
if(elapsed >= 1000L) {
|
if(elapsed >= 1000L) {
|
||||||
Curl_conncache_foreach(data, data->state.conn_cache, data,
|
struct prunedead prune;
|
||||||
call_disconnect_if_dead);
|
prune.data = data;
|
||||||
|
prune.extracted = NULL;
|
||||||
|
while(Curl_conncache_foreach(data, data->state.conn_cache, &prune,
|
||||||
|
call_extract_if_dead)) {
|
||||||
|
/* disconnect it */
|
||||||
|
(void)Curl_disconnect(prune.extracted, /* dead_connection */TRUE);
|
||||||
|
}
|
||||||
data->state.conn_cache->last_cleanup = now;
|
data->state.conn_cache->last_cleanup = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1106,8 +1073,8 @@ ConnectionExists(struct Curl_easy *data,
|
|||||||
Curl_pipeline_site_blacklisted(data, needle))
|
Curl_pipeline_site_blacklisted(data, needle))
|
||||||
canpipe &= ~ CURLPIPE_HTTP1;
|
canpipe &= ~ CURLPIPE_HTTP1;
|
||||||
|
|
||||||
/* Look up the bundle with all the connections to this
|
/* Look up the bundle with all the connections to this particular host.
|
||||||
particular host */
|
Locks the connection cache, beware of early returns! */
|
||||||
bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache);
|
bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache);
|
||||||
if(bundle) {
|
if(bundle) {
|
||||||
/* Max pipe length is zero (unlimited) for multiplexed connections */
|
/* Max pipe length is zero (unlimited) for multiplexed connections */
|
||||||
@ -1130,6 +1097,7 @@ ConnectionExists(struct Curl_easy *data,
|
|||||||
if((bundle->multiuse == BUNDLE_UNKNOWN) && data->set.pipewait) {
|
if((bundle->multiuse == BUNDLE_UNKNOWN) && data->set.pipewait) {
|
||||||
infof(data, "Server doesn't support multi-use yet, wait\n");
|
infof(data, "Server doesn't support multi-use yet, wait\n");
|
||||||
*waitpipe = TRUE;
|
*waitpipe = TRUE;
|
||||||
|
Curl_conncache_unlock(needle);
|
||||||
return FALSE; /* no re-use */
|
return FALSE; /* no re-use */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1161,8 +1129,11 @@ ConnectionExists(struct Curl_easy *data,
|
|||||||
check = curr->ptr;
|
check = curr->ptr;
|
||||||
curr = curr->next;
|
curr = curr->next;
|
||||||
|
|
||||||
if(disconnect_if_dead(check, data))
|
if(extract_if_dead(check, data)) {
|
||||||
|
/* disconnect it */
|
||||||
|
(void)Curl_disconnect(check, /* dead_connection */TRUE);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
pipeLen = check->send_pipe.size + check->recv_pipe.size;
|
pipeLen = check->send_pipe.size + check->recv_pipe.size;
|
||||||
|
|
||||||
@ -1479,9 +1450,13 @@ ConnectionExists(struct Curl_easy *data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(chosen) {
|
if(chosen) {
|
||||||
|
/* mark it as used before releasing the lock */
|
||||||
|
chosen->inuse = TRUE;
|
||||||
|
Curl_conncache_unlock(needle);
|
||||||
*usethis = chosen;
|
*usethis = chosen;
|
||||||
return TRUE; /* yes, we found one to use! */
|
return TRUE; /* yes, we found one to use! */
|
||||||
}
|
}
|
||||||
|
Curl_conncache_unlock(needle);
|
||||||
|
|
||||||
if(foundPendingCandidate && data->set.pipewait) {
|
if(foundPendingCandidate && data->set.pipewait) {
|
||||||
infof(data,
|
infof(data,
|
||||||
@ -4409,20 +4384,21 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||||||
else
|
else
|
||||||
reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse, &waitpipe);
|
reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse, &waitpipe);
|
||||||
|
|
||||||
/* If we found a reusable connection, we may still want to
|
/* If we found a reusable connection that is now marked as in use, we may
|
||||||
open a new connection if we are pipelining. */
|
still want to open a new connection if we are pipelining. */
|
||||||
if(reuse && !force_reuse && IsPipeliningPossible(data, conn_temp)) {
|
if(reuse && !force_reuse && IsPipeliningPossible(data, conn_temp)) {
|
||||||
size_t pipelen = conn_temp->send_pipe.size + conn_temp->recv_pipe.size;
|
size_t pipelen = conn_temp->send_pipe.size + conn_temp->recv_pipe.size;
|
||||||
if(pipelen > 0) {
|
if(pipelen > 0) {
|
||||||
infof(data, "Found connection %ld, with requests in the pipe (%zu)\n",
|
infof(data, "Found connection %ld, with requests in the pipe (%zu)\n",
|
||||||
conn_temp->connection_id, pipelen);
|
conn_temp->connection_id, pipelen);
|
||||||
|
|
||||||
if(conn_temp->bundle->num_connections < max_host_connections &&
|
if(Curl_conncache_bundle_size(conn_temp) < max_host_connections &&
|
||||||
data->state.conn_cache->num_connections < max_total_connections) {
|
Curl_conncache_size(data) < max_total_connections) {
|
||||||
/* We want a new connection anyway */
|
/* We want a new connection anyway */
|
||||||
reuse = FALSE;
|
reuse = FALSE;
|
||||||
|
|
||||||
infof(data, "We can reuse, but we want a new connection anyway\n");
|
infof(data, "We can reuse, but we want a new connection anyway\n");
|
||||||
|
Curl_conncache_return_conn(conn_temp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4434,8 +4410,6 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||||||
* just allocated before we can move along and use the previously
|
* just allocated before we can move along and use the previously
|
||||||
* existing one.
|
* existing one.
|
||||||
*/
|
*/
|
||||||
conn_temp->inuse = TRUE; /* mark this as being in use so that no other
|
|
||||||
handle in a multi stack may nick it */
|
|
||||||
reuse_conn(conn, conn_temp);
|
reuse_conn(conn, conn_temp);
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
free(conn->ssl_extra);
|
free(conn->ssl_extra);
|
||||||
@ -4455,7 +4429,6 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||||||
/* We have decided that we want a new connection. However, we may not
|
/* We have decided that we want a new connection. However, we may not
|
||||||
be able to do that if we have reached the limit of how many
|
be able to do that if we have reached the limit of how many
|
||||||
connections we are allowed to open. */
|
connections we are allowed to open. */
|
||||||
struct connectbundle *bundle = NULL;
|
|
||||||
|
|
||||||
if(conn->handler->flags & PROTOPT_ALPN_NPN) {
|
if(conn->handler->flags & PROTOPT_ALPN_NPN) {
|
||||||
/* The protocol wants it, so set the bits if enabled in the easy handle
|
/* The protocol wants it, so set the bits if enabled in the easy handle
|
||||||
@ -4470,35 +4443,42 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||||||
/* There is a connection that *might* become usable for pipelining
|
/* There is a connection that *might* become usable for pipelining
|
||||||
"soon", and we wait for that */
|
"soon", and we wait for that */
|
||||||
connections_available = FALSE;
|
connections_available = FALSE;
|
||||||
else
|
else {
|
||||||
bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache);
|
/* this gets a lock on the conncache */
|
||||||
|
struct connectbundle *bundle =
|
||||||
|
Curl_conncache_find_bundle(conn, data->state.conn_cache);
|
||||||
|
|
||||||
if(max_host_connections > 0 && bundle &&
|
if(max_host_connections > 0 && bundle &&
|
||||||
(bundle->num_connections >= max_host_connections)) {
|
(bundle->num_connections >= max_host_connections)) {
|
||||||
struct connectdata *conn_candidate;
|
struct connectdata *conn_candidate;
|
||||||
|
|
||||||
/* The bundle is full. Let's see if we can kill a connection. */
|
/* The bundle is full. Extract the oldest connection. */
|
||||||
conn_candidate = find_oldest_idle_connection_in_bundle(data, bundle);
|
conn_candidate = Curl_conncache_extract_bundle(data, bundle);
|
||||||
|
Curl_conncache_unlock(conn);
|
||||||
|
|
||||||
if(conn_candidate) {
|
if(conn_candidate) {
|
||||||
/* Set the connection's owner correctly, then kill it */
|
/* Set the connection's owner correctly, then kill it */
|
||||||
conn_candidate->data = data;
|
conn_candidate->data = data;
|
||||||
(void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
|
(void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
infof(data, "No more connections allowed to host: %d\n",
|
infof(data, "No more connections allowed to host: %d\n",
|
||||||
max_host_connections);
|
max_host_connections);
|
||||||
connections_available = FALSE;
|
connections_available = FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
Curl_conncache_unlock(conn);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(connections_available &&
|
if(connections_available &&
|
||||||
(max_total_connections > 0) &&
|
(max_total_connections > 0) &&
|
||||||
(data->state.conn_cache->num_connections >= max_total_connections)) {
|
(Curl_conncache_size(data) >= max_total_connections)) {
|
||||||
struct connectdata *conn_candidate;
|
struct connectdata *conn_candidate;
|
||||||
|
|
||||||
/* The cache is full. Let's see if we can kill a connection. */
|
/* The cache is full. Let's see if we can kill a connection. */
|
||||||
conn_candidate = Curl_conncache_oldest_idle(data);
|
conn_candidate = Curl_conncache_extract_oldest(data);
|
||||||
|
|
||||||
if(conn_candidate) {
|
if(conn_candidate) {
|
||||||
/* Set the connection's owner correctly, then kill it */
|
/* Set the connection's owner correctly, then kill it */
|
||||||
@ -4521,6 +4501,9 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
/* Mark the connection as used, before we add it */
|
||||||
|
conn->inuse = TRUE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a brand new connection, so let's store it in the connection
|
* This is a brand new connection, so let's store it in the connection
|
||||||
* cache of ours!
|
* cache of ours!
|
||||||
@ -4548,9 +4531,6 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mark the connection as used */
|
|
||||||
conn->inuse = TRUE;
|
|
||||||
|
|
||||||
/* Setup and init stuff before DO starts, in preparing for the transfer. */
|
/* Setup and init stuff before DO starts, in preparing for the transfer. */
|
||||||
Curl_init_do(data, conn);
|
Curl_init_do(data, conn);
|
||||||
|
|
||||||
|
@ -775,9 +775,10 @@ struct connectdata {
|
|||||||
void *closesocket_client;
|
void *closesocket_client;
|
||||||
|
|
||||||
bool inuse; /* This is a marker for the connection cache logic. If this is
|
bool inuse; /* This is a marker for the connection cache logic. If this is
|
||||||
TRUE this handle is being used by an easy handle and cannot
|
TRUE this handle is being used by one or more easy handles
|
||||||
be used by any other easy handle without careful
|
and can only used by any other easy handle without careful
|
||||||
consideration (== only for pipelining). */
|
consideration (== only for pipelining/multiplexing) and it
|
||||||
|
cannot be used by another multi handle! */
|
||||||
|
|
||||||
/**** Fields set when inited and not modified again */
|
/**** Fields set when inited and not modified again */
|
||||||
long connection_id; /* Contains a unique number to make it easier to
|
long connection_id; /* Contains a unique number to make it easier to
|
||||||
@ -1325,6 +1326,9 @@ struct UrlState {
|
|||||||
struct Curl_easy *stream_depends_on;
|
struct Curl_easy *stream_depends_on;
|
||||||
bool stream_depends_e; /* set or don't set the Exclusive bit */
|
bool stream_depends_e; /* set or don't set the Exclusive bit */
|
||||||
int stream_weight;
|
int stream_weight;
|
||||||
|
#ifdef CURLDEBUG
|
||||||
|
bool conncache_lock;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,10 +29,6 @@ run 1: foobar and so on fun!
|
|||||||
<- Mutex unlock
|
<- Mutex unlock
|
||||||
-> Mutex lock
|
-> Mutex lock
|
||||||
<- Mutex unlock
|
<- Mutex unlock
|
||||||
-> Mutex lock
|
|
||||||
<- Mutex unlock
|
|
||||||
-> Mutex lock
|
|
||||||
<- Mutex unlock
|
|
||||||
run 1: foobar and so on fun!
|
run 1: foobar and so on fun!
|
||||||
-> Mutex lock
|
-> Mutex lock
|
||||||
<- Mutex unlock
|
<- Mutex unlock
|
||||||
@ -40,6 +36,10 @@ run 1: foobar and so on fun!
|
|||||||
<- Mutex unlock
|
<- Mutex unlock
|
||||||
-> Mutex lock
|
-> Mutex lock
|
||||||
<- Mutex unlock
|
<- Mutex unlock
|
||||||
|
-> Mutex lock
|
||||||
|
<- Mutex unlock
|
||||||
|
-> Mutex lock
|
||||||
|
<- Mutex unlock
|
||||||
run 1: foobar and so on fun!
|
run 1: foobar and so on fun!
|
||||||
-> Mutex lock
|
-> Mutex lock
|
||||||
<- Mutex unlock
|
<- Mutex unlock
|
||||||
@ -47,11 +47,19 @@ run 1: foobar and so on fun!
|
|||||||
<- Mutex unlock
|
<- Mutex unlock
|
||||||
-> Mutex lock
|
-> Mutex lock
|
||||||
<- Mutex unlock
|
<- Mutex unlock
|
||||||
|
-> Mutex lock
|
||||||
|
<- Mutex unlock
|
||||||
|
-> Mutex lock
|
||||||
|
<- Mutex unlock
|
||||||
run 1: foobar and so on fun!
|
run 1: foobar and so on fun!
|
||||||
-> Mutex lock
|
-> Mutex lock
|
||||||
<- Mutex unlock
|
<- Mutex unlock
|
||||||
-> Mutex lock
|
-> Mutex lock
|
||||||
<- Mutex unlock
|
<- Mutex unlock
|
||||||
|
-> Mutex lock
|
||||||
|
<- Mutex unlock
|
||||||
|
-> Mutex lock
|
||||||
|
<- Mutex unlock
|
||||||
</datacheck>
|
</datacheck>
|
||||||
</reply>
|
</reply>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user