← Commit list

d1823ebc17

Stefan Eissing
Stefan Eissing
2026-09-18
SUCCESS Duration: 424s

Subject

connectionpool: some tweaks

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

lib/cf-https-connect.c+1 -1
@@ -276,7 +276,7 @@ static bool cf_hc_may_h3(struct Curl_cfilter *cf,
struct cf_hc_ctx *ctx = cf->ctx;
if(!ctx->checked_h3) {
ctx->check_h3_result =
- Curl_conn_may_http3(data, cf->conn, ctx->def_transport);
+ Curl_conn_may_http3(data, cf->conn);
ctx->checked_h3 = TRUE;
}
return !ctx->check_h3_result;
lib/conncache.c+196 -137
@@ -449,111 +449,109 @@ static void cpool_evict_conn(struct cpool *cpool,
cpool_conn_close(cpool, admin, conn, FALSE);
}
-int Curl_cpool_check_limits(struct Curl_easy *data,
- struct connectdata *conn,
- const struct curltime *pnow)
+#define CPOOL_LIMIT_OK 0
+#define CPOOL_LIMIT_DEST 1
+#define CPOOL_LIMIT_TOTAL 2
+
+static int cpool_check_limits(struct Curl_easy *data,
+ uint32_t max_total,
+ struct connectdata *to_add,
+ uint32_t max_host,
+ struct cpool_bundle **pbundle,
+ const struct curltime *pnow)
{
struct cpool *cpool = cpool_get_instance(data);
- struct Curl_multi *multi = data->multi;
- struct Curl_easy *admin;
- struct cpool_bundle *bundle;
- size_t dest_limit = 0;
- size_t total_limit = 0;
- size_t shutdowns;
- int res = CPOOL_LIMIT_OK;
+ struct cshutdn *cshutdn = data->multi ? &data->multi->cshutdn : NULL;
+ struct Curl_easy *admin = NULL;
if(!cpool)
return CPOOL_LIMIT_OK;
- /* multi determines the limits, no matter who owns the pool */
- if(multi) {
- dest_limit = multi->max_host_connections;
- total_limit = multi->max_total_connections;
- }
-
- if(!dest_limit && !total_limit)
+ if(!max_total && !max_host)
return CPOOL_LIMIT_OK;
- admin = Curl_get_admin(data);
- CPOOL_LOCK(cpool, admin);
- if(dest_limit) {
- size_t live;
-
- bundle = cpool_find_bundle(cpool, conn->destination);
- live = bundle ? Curl_llist_count(&bundle->conns) : 0;
- shutdowns =
- multi ? Curl_cshutdn_dest_count(&multi->cshutdn, conn->destination) : 0;
- while((live + shutdowns) >= dest_limit) {
- if(shutdowns) {
- /* close one connection in shutdown right away, if we can */
- if(!Curl_cshutdn_close_oldest(&multi->cshutdn, multi->admin,
- conn->destination))
- break;
- }
- else if(!bundle)
- break;
- else {
- struct connectdata *oldest_idle = NULL;
- /* The bundle is full. Extract the oldest connection that may
- * be removed now, if there is one. */
- oldest_idle = cpool_bundle_get_oldest_idle(bundle, pnow);
- if(!oldest_idle)
- break;
- /* disconnect the old conn and continue */
- CURL_TRC_M(admin, "Discarding connection #%" FMT_OFF_T
- " from %zu to reach destination limit of %zu",
- oldest_idle->connection_id,
- Curl_llist_count(&bundle->conns), dest_limit);
- cpool_evict_conn(cpool, admin, oldest_idle);
-
- /* in case the bundle was destroyed in disconnect, look it up again */
- bundle = cpool_find_bundle(cpool, conn->destination);
- live = bundle ? Curl_llist_count(&bundle->conns) : 0;
+ if(max_host) {
+ size_t live = 0, shutdowns = 0;
+ /* if we are at or above `dest_limit`, try to get rid of connections
+ * in shutdown and, if that does not lower it, evict idle connections
+ * from the pool. */
+ admin = Curl_get_admin(data);
+ if(*pbundle) {
+ live = Curl_llist_count(&(*pbundle)->conns);
+ if(live >= max_host) {
+ size_t over = live - max_host + 1;
+ for(; over && *pbundle; --over) {
+ struct connectdata *oldest_idle =
+ cpool_bundle_get_oldest_idle(*pbundle, pnow);
+ if(!oldest_idle)
+ break;
+ /* disconnect the old conn and continue */
+ CURL_TRC_M(admin, "Shutting down connection #%" FMT_OFF_T
+ " to '%s' due to destination limit of %u",
+ oldest_idle->connection_id, oldest_idle->destination,
+ max_host);
+ cpool_evict_conn(cpool, admin, oldest_idle);
+ /* bundle may get destroyed in disconnect, look it up again */
+ *pbundle = cpool_find_bundle(cpool, to_add->destination);
+ }
+ live = *pbundle ? Curl_llist_count(&(*pbundle)->conns) : 0;
}
- shutdowns = multi ?
- Curl_cshutdn_dest_count(&multi->cshutdn, conn->destination) : 0;
}
- if((live + shutdowns) >= dest_limit) {
- res = CPOOL_LIMIT_DEST;
- goto out;
+ if(cshutdn) {
+ shutdowns = Curl_cshutdn_dest_count(cshutdn, to_add->destination);
+ if(shutdowns && ((live + shutdowns) >= max_host)) {
+ size_t over = live + shutdowns - max_host + 1;
+ shutdowns -= Curl_cshutdn_close_oldest(cshutdn, admin,
+ to_add->destination, over);
+ }
}
+ /* We may not have gotten rid of as many connections as we wanted to */
+ if((live + shutdowns) >= max_host)
+ return CPOOL_LIMIT_DEST;
}
- if(total_limit) {
- shutdowns = multi ? Curl_cshutdn_count(&multi->cshutdn) : 0;
- while((cpool->num_conn + shutdowns) >= total_limit) {
- if(shutdowns) {
- /* close one connection in shutdown right away, if we can */
- if(!Curl_cshutdn_close_oldest(&multi->cshutdn, multi->admin, NULL))
- break;
- }
- else {
+ /* Internal transfers are outside the total limit */
+ if(max_total && !data->state.internal) {
+ size_t shutdowns = 0;
+
+ if(!admin)
+ admin = Curl_get_admin(data);
+ if(cpool->num_conn >= max_total) {
+ size_t over = cpool->num_conn - max_total + 1;
+ for(; over; --over) {
struct connectdata *oldest_idle =
cpool_get_oldest_idle(cpool, pnow, 0);
if(!oldest_idle)
break;
/* disconnect the old conn and continue */
- CURL_TRC_M(admin, "Discarding connection #%"
- FMT_OFF_T " from %zu to reach total "
- "limit of %zu",
- oldest_idle->connection_id, cpool->num_conn, total_limit);
+ CURL_TRC_M(admin, "Shutting down idle connection #%"
+ FMT_OFF_T " to '%s' due to total limit of %u",
+ oldest_idle->connection_id, oldest_idle->destination,
+ max_total);
cpool_evict_conn(cpool, admin, oldest_idle);
}
- shutdowns = multi ? Curl_cshutdn_count(&multi->cshutdn) : 0;
+ /* bundle might be gone due to evictions */
+ if(*pbundle)
+ *pbundle = cpool_find_bundle(cpool, to_add->destination);
}
- if((cpool->num_conn + shutdowns) >= total_limit) {
- res = CPOOL_LIMIT_TOTAL;
- goto out;
+ if(cshutdn) {
+ shutdowns = Curl_cshutdn_count(cshutdn);
+ if(shutdowns && (cpool->num_conn + shutdowns) >= max_total) {
+ size_t over = cpool->num_conn + shutdowns - max_total + 1;
+ shutdowns -= Curl_cshutdn_close_oldest(cshutdn, admin, NULL, over);
+ }
}
+ if((cpool->num_conn + shutdowns) >= max_total)
+ return CPOOL_LIMIT_TOTAL;
}
-out:
- CPOOL_UNLOCK(cpool, admin);
- return res;
+ return CPOOL_LIMIT_OK;
}
CURLcode Curl_cpool_add(struct Curl_easy *data,
struct connectdata *conn,
+ uint32_t max_total,
+ uint32_t max_host,
const struct curltime *pnow)
{
CURLcode result = CURLE_OK;
@@ -570,7 +568,24 @@ CURLcode Curl_cpool_add(struct Curl_easy *data,
conn->shutdown.start_ms[SECONDARYSOCKET] = -1;
CPOOL_LOCK(cpool, data);
+
+ /* Find the bundle, should it exist, and check the limits */
bundle = cpool_find_bundle(cpool, conn->destination);
+
+ switch(cpool_check_limits(data, max_total, conn, max_host, &bundle, pnow)) {
+ case CPOOL_LIMIT_DEST:
+ infof(data, "No more connections allowed to host");
+ result = CURLE_NO_CONNECTION_AVAILABLE;
+ goto out;
+ case CPOOL_LIMIT_TOTAL:
+ infof(data, "No connections available, total of %u reached.",
+ data->multi->max_total_connections);
+ result = CURLE_NO_CONNECTION_AVAILABLE;
+ goto out;
+ default:
+ break;
+ }
+
if(!bundle) {
bundle = cpool_add_bundle(cpool, conn->destination);
if(!bundle) {
@@ -645,11 +660,11 @@ static bool cpool_foreach(struct Curl_easy *data,
*
* Return TRUE if idle connection kept in pool, FALSE if closed.
*/
-bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
- struct connectdata *conn,
- const struct curltime *pnow)
+static bool cpool_conn_now_idle(struct cpool *cpool,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct curltime *pnow)
{
- struct cpool *cpool = cpool_get_instance(data);
struct connectdata *oldest_idle = NULL;
struct Curl_easy *admin;
unsigned int maxconnects;
@@ -671,16 +686,8 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
maxconnects = data->multi->maxconnects;
}
- /* remember times, connection had been used just before */
- conn->lastchecked_ms = conn->lastupkeep_ms = conn->lastused_ms =
- curlx_ptimediff_ms(pnow, &conn->created);
if(cpool && maxconnects) {
- /* may be called form a callback already under lock */
- bool do_lock = !CPOOL_IS_LOCKED(cpool);
-
admin = Curl_get_admin(data);
- if(do_lock)
- CPOOL_LOCK(cpool, admin);
if(cpool->num_conn > maxconnects) {
infof(data, "Connection pool is full, closing the oldest of %zu/%u",
cpool->num_conn, maxconnects);
@@ -691,15 +698,47 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
cpool_evict_conn(cpool, admin, oldest_idle);
}
}
- if(do_lock)
- CPOOL_UNLOCK(cpool, admin);
}
return kept;
}
+static void cpool_prune_dead(struct cpool *cpool,
+ struct Curl_easy *data,
+ const struct curltime *pnow);
+
+static bool cpool_find_act(struct cpool *cpool,
+ struct Curl_easy *data,
+ struct cpool_bundle *bundle,
+ struct connectdata *conn,
+ cpool_match_result match)
+{
+ switch(match) {
+ case CPOOL_MATCH_FOUND:
+ return TRUE;
+ case CPOOL_MATCH_TOO_OLD:
+ if(CONN_INUSE(conn))
+ return FALSE;
+ FALLTHROUGH();
+ case CPOOL_MATCH_CLOSE:
+ DEBUGASSERT(conn->bits.in_cpool);
+ if(conn->bits.in_cpool) {
+ cpool_bundle_remove(bundle, conn);
+ conn->bits.in_cpool = FALSE;
+ cpool->num_conn--;
+ }
+ cpool_conn_close(cpool, data, conn, FALSE);
+ break;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
bool Curl_cpool_find(struct Curl_easy *data,
const char *destination,
+ bool prune_dead,
+ const struct curltime *pnow,
Curl_cpool_conn_match_cb *conn_cb,
Curl_cpool_done_match_cb *done_cb,
void *userdata)
@@ -716,6 +755,10 @@ bool Curl_cpool_find(struct Curl_easy *data,
return FALSE;
CPOOL_LOCK(cpool, data);
+
+ if(prune_dead)
+ cpool_prune_dead(cpool, data, pnow);
+
bundle = Curl_hash_pick(&cpool->dest2bundle,
destination, strlen(destination) + 1);
if(bundle) {
@@ -725,31 +768,32 @@ bool Curl_cpool_find(struct Curl_easy *data,
* an authentication involving several requests is using
* the same connection again. */
curr = Curl_llist_head(&bundle->conns);
- while(curr) {
+ while(!found && curr) {
conn = Curl_node_elem(curr);
curr = Curl_node_next(curr);
- if(data->state.lastconnect_id == conn->connection_id) {
- if(conn_cb(conn, userdata))
- found = TRUE;
- break;
- }
+ if(data->state.lastconnect_id != conn->connection_id)
+ continue;
+ found = cpool_find_act(cpool, data, bundle, conn,
+ conn_cb(conn, userdata));
+ break;
}
}
if(!found) {
curr = Curl_llist_head(&bundle->conns);
- while(curr) {
+ while(!found && curr) {
conn = Curl_node_elem(curr);
curr = Curl_node_next(curr);
- /* Already tried a matching connection above. No need to
- * invoke callback again for this one. */
- if((data->state.lastconnect_id != conn->connection_id) &&
- conn_cb(conn, userdata)) {
- found = TRUE;
- break;
- }
+ /* Already tried a matching connection above. */
+ if(data->state.lastconnect_id == conn->connection_id)
+ continue;
+ found = cpool_find_act(cpool, data, bundle, conn,
+ conn_cb(conn, userdata));
}
}
+
+ if(!Curl_llist_count(&bundle->conns))
+ cpool_remove_bundle(cpool, destination);
}
if(done_cb) {
@@ -761,7 +805,7 @@ bool Curl_cpool_find(struct Curl_easy *data,
struct cpool_reaper_ctx {
size_t reaped;
- struct curltime now;
+ const struct curltime *pnow;
};
static int cpool_reap_dead_cb(struct cpool *cpool,
@@ -772,7 +816,7 @@ static int cpool_reap_dead_cb(struct cpool *cpool,
if(!CONN_INUSE(conn)) {
if(conn->bits.no_reuse || conn->bits.close ||
- !Curl_cpool_conn_seems_healthy(conn, admin, &reaper->now)) {
+ !Curl_cpool_conn_seems_healthy(conn, admin, reaper->pnow)) {
/* terminate conn and stop the iteration */
reaper->reaped++;
cpool_conn_close(cpool, admin, conn, FALSE);
@@ -786,32 +830,23 @@ static int cpool_reap_dead_cb(struct cpool *cpool,
* This function scans the data's connection pool for half-open/dead
* connections, closes and removes them.
* The cleanup is done at most once per second.
- *
- * When called, this transfer has no connection attached.
*/
-void Curl_cpool_prune_dead(struct cpool *cpool,
- struct Curl_easy *data)
+static void cpool_prune_dead(struct cpool *cpool,
+ struct Curl_easy *data,
+ const struct curltime *pnow)
{
- struct Curl_easy *admin;
- timediff_t elapsed;
+ timediff_t elapsed_ms = curlx_ptimediff_ms(pnow, &cpool->last_cleanup);
- if(!cpool)
- return;
-
- admin = Curl_get_admin(data);
- CPOOL_LOCK(cpool, admin);
- elapsed = curlx_ptimediff_ms(Curl_pgrs_now(admin), &cpool->last_cleanup);
-
- if(elapsed >= 1000L) {
+ if(elapsed_ms >= 1000L) {
+ struct Curl_easy *admin = Curl_get_admin(data);
struct cpool_reaper_ctx reaper;
memset(&reaper, 0, sizeof(reaper));
- reaper.now = *Curl_pgrs_now(admin);
+ reaper.pnow = pnow;
while(cpool_foreach(admin, cpool, &reaper, cpool_reap_dead_cb))
;
- cpool->last_cleanup = *Curl_pgrs_now(admin);
+ cpool->last_cleanup = *pnow;
}
- CPOOL_UNLOCK(cpool, admin);
}
static int conn_upkeep(struct cpool *cpool,
@@ -890,18 +925,42 @@ struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
return fctx.conn;
}
-void Curl_cpool_do_locked(struct Curl_easy *data,
- struct connectdata *conn,
- Curl_cpool_conn_do_cb *cb, void *cbdata)
+void Curl_cpool_return(struct Curl_easy *data,
+ struct connectdata *conn,
+ Curl_cpool_return_cb *cb, void *cbdata,
+ const struct curltime *pnow)
{
struct cpool *cpool = cpool_get_instance(data);
- if(cpool) {
- CPOOL_LOCK(cpool, data);
- cb(conn, data, cbdata);
- CPOOL_UNLOCK(cpool, data);
+
+ CPOOL_LOCK(cpool, data);
+
+ /* remember times, connection had been used just before */
+ conn->lastchecked_ms = conn->lastupkeep_ms = conn->lastused_ms =
+ curlx_ptimediff_ms(pnow, &conn->created);
+
+ switch(cb(data, conn, cbdata, pnow)) {
+ case CPOOL_DO_KEEP:
+ break;
+ case CPOOL_DO_IDLE:
+ /* the connection is no longer in use by any transfer */
+ if(cpool_conn_now_idle(cpool, data, conn, pnow)) {
+ /* connection kept in the cpool */
+ infof(data, "Connection #%" FMT_OFF_T " to host %s:%u left intact",
+ conn->connection_id, conn->origin->user_hostname,
+ conn->origin->port);
+ }
+ else /* connection was removed from the cpool and destroyed. */
+ data->state.lastconnect_id = -1;
+ break;
+ case CPOOL_DO_CLOSE:
+ Curl_conn_close(data, conn, FALSE);
+ break;
+ case CPOOL_DO_TERMINATE:
+ Curl_conn_close(data, conn, TRUE);
+ break;
}
- else
- cb(conn, data, cbdata);
+
+ CPOOL_UNLOCK(cpool, data);
}
static int cpool_mark_stale(struct cpool *cpool,
@@ -941,9 +1000,9 @@ void Curl_cpool_nw_changed(struct cpool *cpool, struct Curl_easy *admin)
/* A connection has to have been idle for less than 'conn_max_idle_ms'
(the success rate is too low after this), or created less than
'conn_max_age_ms' ago, to be subject for reuse. */
-static bool cpool_conn_maxage(struct Curl_easy *data,
- struct connectdata *conn,
- const struct curltime *pnow)
+static bool cpool_conn_too_old(struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct curltime *pnow)
{
timediff_t age_ms;
@@ -979,7 +1038,7 @@ bool Curl_cpool_conn_seems_healthy(struct connectdata *conn,
bool healthy = TRUE;
DEBUGASSERT(!data->conn);
- if(!CONN_INUSE(conn) && cpool_conn_maxage(data, conn, pnow)) /* too old? */
+ if(!CONN_INUSE(conn) && cpool_conn_too_old(data, conn, pnow))
return FALSE;
if((curlx_ptimediff_ms(pnow, &conn->created) - conn->lastchecked_ms) < 1000)
return TRUE;
lib/conncache.h+29 -37
@@ -83,6 +83,8 @@ struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
/* Add the connection to the pool. */
CURLcode Curl_cpool_add(struct Curl_easy *data,
struct connectdata *conn,
+ uint32_t max_total,
+ uint32_t max_host,
const struct curltime *pnow) WARN_UNUSED_RESULT;
/* Connection was used at the given time. */
@@ -95,21 +97,16 @@ timediff_t Curl_cpool_conn_age_ms(struct Curl_easy *data,
struct connectdata *conn,
const struct curltime *pnow);
-/**
- * Return if the pool has reached its configured limits for adding
- * the given connection. Try to discard the oldest, idle connections
- * to make space.
- */
-#define CPOOL_LIMIT_OK 0
-#define CPOOL_LIMIT_DEST 1
-#define CPOOL_LIMIT_TOTAL 2
-int Curl_cpool_check_limits(struct Curl_easy *data,
- struct connectdata *conn,
- const struct curltime *pnow);
+typedef enum {
+ CPOOL_MATCH_FOUND, /* The passed `conn` matches, stop looking further */
+ CPOOL_MATCH_CONT, /* No match, continue looking */
+ CPOOL_MATCH_CLOSE, /* No match, close `conn`, continue looking */
+ CPOOL_MATCH_TOO_OLD /* No match, `conn` is too old, close when idle */
+} cpool_match_result;
/* Return of conn is suitable. If so, stops iteration. */
-typedef bool Curl_cpool_conn_match_cb(struct connectdata *conn,
- void *userdata);
+typedef cpool_match_result
+ Curl_cpool_conn_match_cb(struct connectdata *conn, void *userdata);
/* Act on the result of the find, may override it. */
typedef bool Curl_cpool_done_match_cb(void *userdata);
@@ -119,6 +116,8 @@ typedef bool Curl_cpool_done_match_cb(void *userdata);
* All callbacks are invoked while the pool's lock is held.
* @param data current transfer
* @param destination match against `conn->destination` in pool
+ * @param prune_dead perform reaping of dead connections at start
+ * @param pnow timestamp of operation
* @param conn_cb must be present, called for each connection in the
* bundle until it returns TRUE
* @return combined result of last conn_db and result_cb or FALSE if no
@@ -126,36 +125,28 @@ typedef bool Curl_cpool_done_match_cb(void *userdata);
*/
bool Curl_cpool_find(struct Curl_easy *data,
const char *destination,
+ bool prune_dead,
+ const struct curltime *pnow,
Curl_cpool_conn_match_cb *conn_cb,
Curl_cpool_done_match_cb *done_cb,
void *userdata);
-/*
- * A connection (already in the pool) is now idle. Do any
- * cleanups in regard to the pool's limits.
- *
- * Return TRUE if idle connection kept in pool, FALSE if closed.
- */
-bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
- struct connectdata *conn,
- const struct curltime *pnow);
-
-/**
- * Scans the connection pool for half-open/dead
- * connections, closes and removes them.
- * The cleanup is done at most once per second.
- */
-void Curl_cpool_prune_dead(struct cpool *cpool,
- struct Curl_easy *data);
-
/**
* Perform upkeep actions on connections in the transfer's pool.
*/
CURLcode Curl_cpool_upkeep(struct Curl_easy *data);
-typedef void Curl_cpool_conn_do_cb(struct connectdata *conn,
- struct Curl_easy *data,
- void *cbdata);
+typedef enum {
+ CPOOL_DO_KEEP, /* Keep the connection, still in use */
+ CPOOL_DO_IDLE, /* Connection is idle, may get closed now */
+ CPOOL_DO_CLOSE, /* Close the connection (clean) */
+ CPOOL_DO_TERMINATE /* Terminate the connection (unclean) */
+} cpool_do_result;
+
+typedef cpool_do_result Curl_cpool_return_cb(struct Curl_easy *data,
+ struct connectdata *conn,
+ void *cbdata,
+ const struct curltime *pnow);
/**
* Invoked the callback for the given data + connection under the
@@ -163,9 +154,10 @@ typedef void Curl_cpool_conn_do_cb(struct connectdata *conn,
* The callback is always invoked, even if the transfer has no connection
* pool associated.
*/
-void Curl_cpool_do_locked(struct Curl_easy *data,
- struct connectdata *conn,
- Curl_cpool_conn_do_cb *cb, void *cbdata);
+void Curl_cpool_return(struct Curl_easy *data,
+ struct connectdata *conn,
+ Curl_cpool_return_cb *cb, void *cbdata,
+ const struct curltime *pnow);
/* Close all unused connections, prevent reuse of existing ones. */
void Curl_cpool_nw_changed(struct cpool *cpool, struct Curl_easy *admin);
lib/cshutdn.c+26 -28
@@ -277,42 +277,40 @@ void Curl_cshutdn_terminate(struct Curl_easy *admin,
}
}
-static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn,
- struct Curl_easy *admin,
- const char *destination)
+static size_t cshutdn_destroy_oldest(struct cshutdn *cshutdn,
+ struct Curl_easy *admin,
+ const char *destination,
+ size_t count)
{
- struct Curl_llist_node *e;
- struct connectdata *conn;
-
- e = Curl_llist_head(&cshutdn->list);
- while(e) {
- conn = Curl_node_elem(e);
- if(!destination || !strcmp(destination, conn->destination))
- break;
- e = Curl_node_next(e);
- }
+ struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list), *next;
+ struct Curl_sigpipe_ctx sigpipe_ctx;
+ size_t n = 0;
- if(e) {
- struct Curl_sigpipe_ctx sigpipe_ctx;
- conn = Curl_node_elem(e);
- Curl_node_remove(e);
- sigpipe_init(&sigpipe_ctx);
- sigpipe_apply(admin, &sigpipe_ctx);
- Curl_cshutdn_terminate(admin, conn, FALSE);
- sigpipe_restore(&sigpipe_ctx);
- return TRUE;
+ sigpipe_init(&sigpipe_ctx);
+ while(e && (n < count)) {
+ struct connectdata *conn = Curl_node_elem(e);
+ next = Curl_node_next(e);
+ if(!destination || !strcmp(destination, conn->destination)) {
+ Curl_node_remove(e);
+ sigpipe_apply(admin, &sigpipe_ctx);
+ Curl_cshutdn_terminate(admin, conn, FALSE);
+ ++n;
+ }
+ e = next;
}
- return FALSE;
+ sigpipe_restore(&sigpipe_ctx);
+ return n;
}
-bool Curl_cshutdn_close_oldest(struct cshutdn *cshutdn,
+size_t Curl_cshutdn_close_oldest(struct cshutdn *cshutdn,
struct Curl_easy *admin,
- const char *destination)
+ const char *destination,
+ size_t count)
{
if(cshutdn) {
- return cshutdn_destroy_oldest(cshutdn, admin, destination);
+ return cshutdn_destroy_oldest(cshutdn, admin, destination, count);
}
- return FALSE;
+ return 0;
}
#define NUM_POLLS_ON_STACK 10
@@ -502,7 +500,7 @@ void Curl_cshutdn_add(struct cshutdn *cshutdn,
if(max_shutdowns <= Curl_llist_count(&cshutdn->list)) {
CURL_TRC_M(admin, "[SHUTDOWN] discarding oldest shutdown connection "
"due to shutdown limit of %zu", max_shutdowns);
- cshutdn_destroy_oldest(cshutdn, admin, NULL);
+ cshutdn_destroy_oldest(cshutdn, admin, NULL, 1);
}
if(multi->socket_cb && cshutdn_update_ev(multi, conn)) {
lib/cshutdn.h+6 -5
@@ -92,12 +92,13 @@ size_t Curl_cshutdn_count(struct cshutdn *cshutdn);
size_t Curl_cshutdn_dest_count(struct cshutdn *cshutdn,
const char *destination);
-/* Close the oldest connection in shutdown to destination or,
+/* Close the oldest `count` connections in shutdown to destination or,
* when destination is NULL for any destination.
- * Return TRUE if a connection has been closed. */
-bool Curl_cshutdn_close_oldest(struct cshutdn *cshutdn,
- struct Curl_easy *admin,
- const char *destination);
+ * Return the number of connections closed. */
+size_t Curl_cshutdn_close_oldest(struct cshutdn *cshutdn,
+ struct Curl_easy *admin,
+ const char *destination,
+ size_t count);
/* Add a connection to have it shut down. Terminate the oldest
* connection when shutdowns exceed max_shutdowns. */
lib/http.c+1 -1
@@ -130,7 +130,7 @@ CURLcode Curl_http_setup_conn(struct Curl_easy *data,
during this request */
if(data->state.http_neg.wanted == CURL_HTTP_V3x) {
/* only HTTP/3, needs to work */
- CURLcode result = Curl_conn_may_http3(data, conn, conn->transport_wanted);
+ CURLcode result = Curl_conn_may_http3(data, conn);
if(result)
return result;
}
lib/multi.c+19 -29
@@ -635,27 +635,22 @@ static bool multi_conn_should_close(struct connectdata *conn,
return FALSE;
}
-static void multi_done_locked(struct connectdata *conn,
- struct Curl_easy *data,
- void *userdata)
+static cpool_do_result multi_done_locked(struct Curl_easy *data,
+ struct connectdata *conn,
+ void *userdata,
+ const struct curltime *pnow)
{
struct multi_done_ctx *mdctx = userdata;
- const struct curltime *pnow = Curl_pgrs_now(data);
-
- Curl_detach_connection(data);
+ (void)pnow;
CURL_TRC_M(data, "multi_done_locked, in use=%u", conn->attached_xfers);
if(CONN_INUSE(conn)) {
/* Stop if still used. */
CURL_TRC_M(data, "Connection still in use %u, no more multi_done now!",
conn->attached_xfers);
- return;
+ return CPOOL_DO_KEEP;
}
- data->state.done = TRUE; /* called now! */
-
- Curl_dnscache_prune(data, pnow);
-
if(multi_conn_should_close(conn, data, (bool)mdctx->premature)) {
CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s:%u, "
"forbid=%d, close=%d, premature=%d, conn_multiplex=%d",
@@ -664,27 +659,17 @@ static void multi_done_locked(struct connectdata *conn,
data->set.reuse_forbid, conn->bits.close, mdctx->premature,
Curl_conn_is_multiplex(conn, FIRSTSOCKET));
connclose(conn);
- Curl_conn_close(data, conn, (bool)mdctx->premature);
+ return mdctx->premature ? CPOOL_DO_TERMINATE : CPOOL_DO_CLOSE;
}
else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) {
CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s:%u was shutdown"
" by server, not reusing", conn->connection_id,
conn->origin->user_hostname, conn->origin->port);
connclose(conn);
- Curl_conn_close(data, conn, (bool)mdctx->premature);
+ return mdctx->premature ? CPOOL_DO_TERMINATE : CPOOL_DO_CLOSE;
}
else {
- /* the connection is no longer in use by any transfer */
- if(Curl_cpool_conn_now_idle(data, conn, pnow)) {
- /* connection kept in the cpool */
- infof(data, "Connection #%" FMT_OFF_T " to host %s:%u left intact",
- conn->connection_id, conn->origin->user_hostname,
- conn->origin->port);
- }
- else {
- /* connection was removed from the cpool and destroyed. */
- data->state.lastconnect_id = -1;
- }
+ return CPOOL_DO_IDLE;
}
}
@@ -695,13 +680,16 @@ static CURLcode multi_done(struct Curl_easy *data,
{
CURLcode result;
struct connectdata *conn = data->conn;
+ const struct curltime *pnow = Curl_pgrs_now(data);
CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d",
(int)status, (int)premature, data->state.done);
- if(data->state.done)
+ if(data->state.done) {
/* Stop if multi_done() has already been called */
return CURLE_OK;
+ }
+ data->state.done = TRUE; /* called now! */
/* Shut down any ongoing async resolver operation. */
Curl_resolv_shutdown_all(data);
@@ -736,7 +724,7 @@ static CURLcode multi_done(struct Curl_easy *data,
* - the transfer has not connected
* - we already aborted by callback to avoid this calling another callback
*/
- int rc = Curl_pgrsDone(data);
+ int rc = Curl_pgrsDone(data, pnow);
if(!result && rc)
result = CURLE_ABORTED_BY_CALLBACK;
}
@@ -754,13 +742,15 @@ static CURLcode multi_done(struct Curl_easy *data,
result = Curl_req_done(&data->req, data, premature);
if(conn) {
- /* Under the potential connection pool's share lock, decide what to
- * do with the transfer's connection. */
+ /* Detach connection and decide what to do with it. */
struct multi_done_ctx mdctx;
+ Curl_detach_connection(data);
+ Curl_dnscache_prune(data, pnow);
+
memset(&mdctx, 0, sizeof(mdctx));
mdctx.premature = premature;
- Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx);
+ Curl_cpool_return(data, conn, multi_done_locked, &mdctx, pnow);
}
/* flush the netrc cache */
lib/progress.c+2 -2
@@ -185,11 +185,11 @@ const struct curltime *Curl_pgrs_now(struct Curl_easy *data)
pgrsDone() - transfer complete
*/
-int Curl_pgrsDone(struct Curl_easy *data)
+int Curl_pgrsDone(struct Curl_easy *data, const struct curltime *pnow)
{
int rc;
data->progress.delta.lastshow_us = -1;
- rc = Curl_pgrsUpdate(data); /* the final (forced) update */
+ rc = Curl_pgrsUpdateX(data, pnow); /* the final (forced) update */
if(rc)
return rc;
lib/progress.h+1 -1
@@ -45,7 +45,7 @@ typedef enum {
/* Get the current timestamp of the transfer */
const struct curltime *Curl_pgrs_now(struct Curl_easy *data);
-int Curl_pgrsDone(struct Curl_easy *data);
+int Curl_pgrsDone(struct Curl_easy *data, const struct curltime *pnow);
void Curl_pgrsStart(struct Curl_easy *data, const struct curltime *pnow);
void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size);
void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size);
lib/tftp.c+1 -1
@@ -1017,7 +1017,7 @@ static CURLcode tftp_done(struct Curl_easy *data, CURLcode status,
(void)status;
(void)premature;
- if(Curl_pgrsDone(data))
+ if(Curl_pgrsDone(data, Curl_pgrs_now(data)))
return CURLE_ABORTED_BY_CALLBACK;
/* If we have encountered an error */
lib/url.c+76 -99
@@ -983,50 +983,51 @@ static bool url_match_auth_nego(struct connectdata *conn,
#define url_match_auth_nego(c, m) ((void)(c), (void)(m), TRUE)
#endif
-static bool url_match_conn(struct connectdata *conn, void *userdata)
+static cpool_match_result url_match_conn(struct connectdata *conn,
+ void *userdata)
{
struct url_conn_match *m = userdata;
bool wait_pipe = FALSE;
/* general connect config setting match? */
if(!url_match_connect_config(conn, m))
- return FALSE;
+ return CPOOL_MATCH_CONT;
/* match for destination and protocol? */
if(!url_match_destination(conn, m))
- return FALSE;
+ return CPOOL_MATCH_CONT;
if(!url_match_fully_connected(conn, m))
- return FALSE;
+ return CPOOL_MATCH_CONT;
if(!url_match_multiplex_needs(conn, m))
- return FALSE;
+ return CPOOL_MATCH_CONT;
if(!url_match_ssl_use(conn, m))
- return FALSE;
+ return CPOOL_MATCH_CONT;
if(!url_match_proxy_use(conn, m))
- return FALSE;
+ return CPOOL_MATCH_CONT;
if(!url_match_ssl_config(conn, m))
- return FALSE;
+ return CPOOL_MATCH_CONT;
if(!url_match_http_multiplex(conn, m, &wait_pipe))
- return FALSE;
+ return CPOOL_MATCH_CONT;
if(!url_match_auth(conn, m))
- return FALSE;
+ return CPOOL_MATCH_CONT;
if(!url_match_proto_config(conn, m))
- return FALSE;
+ return CPOOL_MATCH_CONT;
if(!url_match_auth_ntlm(conn, m))
- return FALSE;
+ return CPOOL_MATCH_CONT;
if(!url_match_auth_nego(conn, m))
- return FALSE;
+ return CPOOL_MATCH_CONT;
if(!url_match_multiplex_limits(conn, m))
- return FALSE;
+ return CPOOL_MATCH_CONT;
/* The connection matches all conditions, but do we want to use it? */
if(wait_pipe) {
@@ -1034,16 +1035,14 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
* yet. Put the transfer into PENDING and wait for conn state change. */
DEBUGASSERT(!m->found);
m->wait_pipe = TRUE;
- return TRUE;
+ return CPOOL_MATCH_FOUND;
}
if(m->data->set.conn_max_age_ms > 0) {
timediff_t age_ms = Curl_cpool_conn_age_ms(m->data, conn, &m->now);
if(age_ms > m->data->set.conn_max_age_ms) {
/* Transfer is looking for a younger connection. */
- if(!CONN_INUSE(conn))
- Curl_conn_close(m->data, conn, FALSE);
- return FALSE;
+ return CPOOL_MATCH_TOO_OLD;
}
}
@@ -1053,13 +1052,12 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
!Curl_cpool_conn_seems_healthy(conn, m->data, &m->now)) {
infof(m->data, "Connection %" FMT_OFF_T " seems to be dead, terminating",
conn->connection_id);
- Curl_conn_close(m->data, conn, FALSE);
- return FALSE;
+ return CPOOL_MATCH_CLOSE;
}
/* conn matches our needs. */
m->found = conn;
- return TRUE;
+ return CPOOL_MATCH_FOUND;
}
static bool url_match_result(void *userdata)
@@ -1096,21 +1094,13 @@ static bool url_match_result(void *userdata)
*/
static bool url_attach_existing(struct Curl_easy *data,
struct connectdata *needle,
- struct url_conn_match *m)
+ struct url_conn_match *m,
+ const struct curltime *pnow)
{
- struct cpool *cpool = Curl_cpool_get_instance(data);
- bool success;
-
- DEBUGASSERT(!data->conn);
-
- Curl_cpool_prune_dead(cpool, data);
-
/* Find a connection in the pool that matches what "data + needle"
* requires. If a suitable candidate is found, it is attached to "data". */
- success = Curl_cpool_find(data, needle->destination,
- url_match_conn, url_match_result, m);
-
- return success;
+ return Curl_cpool_find(data, needle->destination, TRUE, pnow,
+ url_match_conn, url_match_result, m);
}
/*
@@ -1522,10 +1512,9 @@ static CURLcode setup_connection_internals(struct Curl_easy *data,
struct connectdata *conn)
{
struct Curl_peer *peer = NULL;
- CURLcode result;
if(conn->scheme->run->setup_connection) {
- result = conn->scheme->run->setup_connection(data, conn);
+ CURLcode result = conn->scheme->run->setup_connection(data, conn);
if(result)
return result;
}
@@ -1556,10 +1545,10 @@ static CURLcode setup_connection_internals(struct Curl_easy *data,
if(data->set.scope_id)
conn->scope_id = data->set.scope_id;
else {
- struct Curl_peer *first = Curl_conn_get_first_peer(conn, FIRSTSOCKET);
- if(!first)
+ peer = Curl_conn_get_first_peer(conn, FIRSTSOCKET);
+ if(!peer)
return CURLE_FAILED_INIT;
- conn->scope_id = first->scopeid;
+ conn->scope_id = peer->scopeid;
}
#endif
@@ -2269,7 +2258,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data,
goto out;
/* Setup a "faked" transfer that will do nothing */
- result = Curl_cpool_add(data, needle, pnow);
+ result = Curl_cpool_add(data, needle, 0, 0, pnow);
Curl_attach_connection(data, needle, TRUE);
needle = NULL;
if(!result) {
@@ -2299,40 +2288,42 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data,
if((!data->set.reuse_fresh || data->state.followlocation) &&
!data->set.connect_only) {
/* Ok, try to find and attach an existing one */
- url_attach_existing(data, needle, &match);
- }
-
- if(data->conn) {
- /* We attached an existing connection for this transfer. Copy
- * over transfer specific properties over from needle. */
- struct connectdata *conn = data->conn;
- VERBOSE(bool tls_upgraded =
- (!(needle->origin->scheme->flags & PROTOPT_SSL) &&
- Curl_conn_is_ssl(conn, FIRSTSOCKET)));
-
- conn->bits.reuse = TRUE;
- url_conn_reuse_adjust(data, needle);
+ if(url_attach_existing(data, needle, &match, pnow)) {
+ /* We attached an existing connection for this transfer. Copy
+ * over transfer specific properties over from needle. */
+ struct connectdata *conn = data->conn;
+ VERBOSE(bool tls_upgraded =
+ (!(needle->origin->scheme->flags & PROTOPT_SSL) &&
+ Curl_conn_is_ssl(conn, FIRSTSOCKET)));
+
+ DEBUGASSERT(conn);
+ conn->bits.reuse = TRUE;
+ url_conn_reuse_adjust(data, needle);
#ifndef CURL_DISABLE_PROXY
- infof(data, "Reusing existing %s: connection%s with %s %s",
- conn->origin->scheme->name,
- tls_upgraded ? " (upgraded to SSL)" : "",
- (conn->socks_proxy.peer || conn->http_proxy.peer) ? "proxy" : "host",
- conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname :
- conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname :
- conn->origin->hostname);
+ infof(data, "Reusing existing %s: connection%s with %s %s",
+ conn->origin->scheme->name,
+ tls_upgraded ? " (upgraded to SSL)" : "",
+ (conn->socks_proxy.peer || conn->http_proxy.peer) ?
+ "proxy" : "host",
+ conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname :
+ conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname :
+ conn->origin->hostname);
#else
- infof(data, "Reusing existing %s: connection%s with host %s",
- conn->origin->scheme->name,
- tls_upgraded ? " (upgraded to SSL)" : "",
- conn->origin->hostname);
+ infof(data, "Reusing existing %s: connection%s with host %s",
+ conn->origin->scheme->name,
+ tls_upgraded ? " (upgraded to SSL)" : "",
+ conn->origin->hostname);
#endif
+ }
}
- else {
- /* We have decided that we want a new connection. We may not be able to do
- that if we have reached the limit of how many connections we are
- allowed to open. */
+ if(!data->conn) {
+ /* We have not attached an existing connection. `needle` may become
+ * the connection to use, if
+ * - we are not waiting on an existing connection result
+ * - the total/host limits on active connections allow it
+ */
if(match.wait_pipe) {
/* There is a connection that *might* become usable for multiplexing
"soon", and we wait for that */
@@ -2340,50 +2331,34 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data,
result = CURLE_NO_CONNECTION_AVAILABLE;
goto out;
}
- else {
- switch(Curl_cpool_check_limits(data, needle, pnow)) {
- case CPOOL_LIMIT_DEST:
- infof(data, "No more connections allowed to host");
- result = CURLE_NO_CONNECTION_AVAILABLE;
- goto out;
- case CPOOL_LIMIT_TOTAL:
- if(data->master_mid != UINT32_MAX)
- CURL_TRC_M(data, "Allowing sub-requests (like DoH) to override "
- "max connection limit");
- else {
- infof(data, "No connections available, total of %u reached.",
- data->multi->max_total_connections);
- result = CURLE_NO_CONNECTION_AVAILABLE;
- goto out;
- }
- break;
- default:
- break;
- }
- }
- /* Convert needle into a full connection by cloning the
- * ssl filter config used in matching into the connection. */
+ /* Add needle to conn pool, observing the limits.
+ * Initializes connection_id.
+ * May fail due to limits with CURLE_NO_CONNECTION_AVAILABLE. */
+ result = Curl_cpool_add(data, needle,
+ data->multi->max_total_connections,
+ data->multi->max_host_connections,
+ pnow);
+ if(result)
+ goto out;
+
+ /* All fine, attach it and forget needle */
+ Curl_attach_connection(data, needle, TRUE);
+ needle = NULL;
+
+ /* Clone the SSL filter configs into the new connection. */
result = Curl_ssl_conn_config_clone(&match.ssl_config,
#ifndef CURL_DISABLE_PROXY
&match.proxy_ssl_config,
#else
NULL,
#endif
- needle);
+ data->conn);
if(result) {
DEBUGF(infof(data, "Error: clone connection SSL config"));
goto out;
}
- /* Add needle to conn pool, which assigns the connection id.
- * Attach regardless of result, for correct handling. */
- result = Curl_cpool_add(data, needle, pnow);
- Curl_attach_connection(data, needle, TRUE);
- needle = NULL;
- if(result)
- goto out;
-
#ifdef USE_NTLM
/* If NTLM is requested in a part of this connection, make sure we do not
assume the state is fine as this is a fresh connection and NTLM is
@@ -2404,6 +2379,8 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data,
#endif
}
+ /* COMMON setup code for reused and new connections from here on. */
+
/* Setup and init stuff before DO starts, in preparing for the transfer. */
result = Curl_init_transfer(data, data->conn);
if(result)
lib/vquic/vquic.c+4 -6
@@ -51,6 +51,7 @@
#include "bufq.h"
#include "curlx/dynbuf.h"
#include "curlx/win32-fopen.h"
+#include "connect.h"
#include "cfilters.h"
#include "vdns/cf-dns.h"
#include "vquic/cf-ngtcp2.h"
@@ -1192,10 +1193,9 @@ CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf,
#endif /* !CURL_DISABLE_PROXY && USE_PROXY_HTTP3 */
CURLcode Curl_conn_may_http3(struct Curl_easy *data,
- const struct connectdata *conn,
- unsigned char transport)
+ struct connectdata *conn)
{
- if(transport == TRNSPRT_UNIX) {
+ if(Curl_conn_get_first_peer(conn, FIRSTSOCKET)->unix_socket) {
failf(data, "HTTP/3 cannot be used over UNIX domain sockets");
return CURLE_QUIC_CONNECT_ERROR;
}
@@ -1324,12 +1324,10 @@ struct nghttp3_mem *Curl_nghttp3_mem(void)
#else /* CURL_DISABLE_HTTP || !USE_HTTP3 */
CURLcode Curl_conn_may_http3(struct Curl_easy *data,
- const struct connectdata *conn,
- unsigned char transport)
+ struct connectdata *conn)
{
(void)data;
(void)conn;
- (void)transport;
DEBUGF(infof(data, "QUIC is not supported in this build"));
return CURLE_NOT_BUILT_IN;
}
lib/vquic/vquic.h+1 -2
@@ -86,7 +86,6 @@ extern struct Curl_cftype Curl_cft_h3_proxy;
#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 */
CURLcode Curl_conn_may_http3(struct Curl_easy *data,
- const struct connectdata *conn,
- unsigned char transport);
+ struct connectdata *conn);
#endif /* HEADER_CURL_VQUIC_QUIC_H */
lib/vssh/libssh.c+1 -1
@@ -2760,7 +2760,7 @@ static CURLcode myssh_done(struct Curl_easy *data,
else
result = status;
- if(Curl_pgrsDone(data))
+ if(Curl_pgrsDone(data, Curl_pgrs_now(data)))
return CURLE_ABORTED_BY_CALLBACK;
CURL_REQ_CLEAR_IO(data);
lib/vssh/libssh2.c+1 -1
@@ -3620,7 +3620,7 @@ static CURLcode ssh_done(struct Curl_easy *data, CURLcode status)
else
result = status;
- if(Curl_pgrsDone(data))
+ if(Curl_pgrsDone(data, Curl_pgrs_now(data)))
return CURLE_ABORTED_BY_CALLBACK;
CURL_REQ_CLEAR_IO(data);
tests/data/test1554+0 -6
@@ -28,8 +28,6 @@ run 1: foobar and so on fun!
[- Mutex unlock CONNECT
-] Mutex lock CONNECT
[- Mutex unlock CONNECT
--] Mutex lock CONNECT
-[- Mutex unlock CONNECT
run 1: foobar and so on fun!
-] Mutex lock CONNECT
[- Mutex unlock CONNECT
@@ -41,8 +39,6 @@ run 1: foobar and so on fun!
[- Mutex unlock CONNECT
-] Mutex lock CONNECT
[- Mutex unlock CONNECT
--] Mutex lock CONNECT
-[- Mutex unlock CONNECT
run 1: foobar and so on fun!
-] Mutex lock CONNECT
[- Mutex unlock CONNECT
@@ -54,8 +50,6 @@ run 1: foobar and so on fun!
[- Mutex unlock CONNECT
-] Mutex lock CONNECT
[- Mutex unlock CONNECT
--] Mutex lock CONNECT
-[- Mutex unlock CONNECT
run 1: foobar and so on fun!
-] Mutex lock CONNECT
[- Mutex unlock CONNECT