]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
conncache: connection shutdown, multi_socket handling
authorStefan Eissing <stefan@eissing.org>
Mon, 22 Jul 2024 15:04:30 +0000 (17:04 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 23 Jul 2024 08:29:07 +0000 (10:29 +0200)
- implement the socket hash user/reader/writer processing also
  for connections that are being shut down by the connection cache.
- split out handling of current vs. last pollset socket event handling
  into a function available in other code parts
- add `shutdown_poll` pollset to `connectdata` struct so that changes
  in the pollset can be recorded during shutdown. (The internal handle
  cannot keep it since it might be used for many connections)

Reported-by: calvin2021y on github
Fixes #14252
Closes #14257

lib/conncache.c
lib/multi.c
lib/multiif.h
lib/urldata.h

index d3f85b3a06ab0574cd354730993a87512c5488d7..a1c44cf04444ce855336e63f12f13c6e21615570 100644 (file)
@@ -62,9 +62,9 @@ static void connc_run_conn_shutdown(struct Curl_easy *data,
                                     bool *done);
 static void connc_run_conn_shutdown_handler(struct Curl_easy *data,
                                             struct connectdata *conn);
-static CURLcode connc_update_shutdown_ev(struct Curl_multi *multi,
-                                         struct Curl_easy *data,
-                                         struct connectdata *conn);
+static CURLMcode connc_update_shutdown_ev(struct Curl_multi *multi,
+                                          struct Curl_easy *data,
+                                          struct connectdata *conn);
 static void connc_shutdown_all(struct conncache *connc, int timeout_ms);
 
 static CURLcode bundle_create(struct connectbundle **bundlep)
@@ -725,7 +725,10 @@ static void connc_discard_conn(struct conncache *connc,
 
   if(data->multi && data->multi->socket_cb) {
     DEBUGASSERT(connc == &data->multi->conn_cache);
-    if(connc_update_shutdown_ev(data->multi, data, conn)) {
+    /* Start with an empty shutdown pollset, so out internal closure handle
+     * is added to the sockets. */
+    memset(&conn->shutdown_poll, 0, sizeof(conn->shutdown_poll));
+    if(connc_update_shutdown_ev(data->multi, connc->closure_handle, conn)) {
       DEBUGF(infof(data, "[CCACHE] update events for shutdown failed, "
                          "discarding #%" CURL_FORMAT_CURL_OFF_T,
                          conn->connection_id));
@@ -738,11 +741,6 @@ static void connc_discard_conn(struct conncache *connc,
   DEBUGF(infof(data, "[CCACHE] added #%" CURL_FORMAT_CURL_OFF_T
                      " to shutdown list of length %zu", conn->connection_id,
                      Curl_llist_count(&connc->shutdowns.conn_list)));
-
-  /* Forget what this transfer last polled, the connection is ours now.
-   * If we do not clear this, the event handling for `data` will tell
-   * the callback to remove the connection socket after we return here. */
-  memset(&data->last_poll, 0, sizeof(data->last_poll));
 }
 
 void Curl_conncache_disconnect(struct Curl_easy *data,
@@ -967,21 +965,16 @@ static void connc_disconnect(struct Curl_easy *data,
   /* the transfer must be detached from the connection */
   DEBUGASSERT(data && !data->conn);
 
+  Curl_attach_connection(data, conn);
+
   if(connc && connc->multi && connc->multi->socket_cb) {
-    unsigned int i;
-    for(i = 0; i < 2; ++i) {
-      if(CURL_SOCKET_BAD == conn->sock[i])
-        continue;
-      /* remove all connection's sockets from event handling */
-      connc->multi->in_callback = TRUE;
-      connc->multi->socket_cb(data, conn->sock[i], CURL_POLL_REMOVE,
-                              connc->multi->socket_userp, NULL);
-      connc->multi->in_callback = FALSE;
-    }
+    struct easy_pollset ps;
+    /* With an empty pollset, all previously polled sockets will be removed
+     * via the multi_socket API callback. */
+    memset(&ps, 0, sizeof(ps));
+    (void)Curl_multi_pollset_ev(connc->multi, data, &ps, &conn->shutdown_poll);
   }
 
-  Curl_attach_connection(data, conn);
-
   connc_run_conn_shutdown_handler(data, conn);
   if(do_shutdown) {
     /* Make a last attempt to shutdown handlers and filters, if
@@ -1003,13 +996,12 @@ static void connc_disconnect(struct Curl_easy *data,
 }
 
 
-static CURLcode connc_update_shutdown_ev(struct Curl_multi *multi,
-                                         struct Curl_easy *data,
-                                         struct connectdata *conn)
+static CURLMcode connc_update_shutdown_ev(struct Curl_multi *multi,
+                                          struct Curl_easy *data,
+                                          struct connectdata *conn)
 {
   struct easy_pollset ps;
-  unsigned int i;
-  int rc;
+  CURLMcode mresult;
 
   DEBUGASSERT(data);
   DEBUGASSERT(multi);
@@ -1020,22 +1012,11 @@ static CURLcode connc_update_shutdown_ev(struct Curl_multi *multi,
   Curl_conn_adjust_pollset(data, &ps);
   Curl_detach_connection(data);
 
-  if(!ps.num)
-    return CURLE_FAILED_INIT;
-
-  for(i = 0; i < ps.num; ++i) {
-    DEBUGF(infof(data, "[CCACHE] set socket=%" CURL_FORMAT_SOCKET_T
-                 " events=%d on #%" CURL_FORMAT_CURL_OFF_T,
-                 ps.sockets[i], ps.actions[i], conn->connection_id));
-    multi->in_callback = TRUE;
-    rc = multi->socket_cb(data, ps.sockets[i], ps.actions[i],
-                          multi->socket_userp, NULL);
-    multi->in_callback = FALSE;
-    if(rc == -1)
-      return CURLE_FAILED_INIT;
-  }
+  mresult = Curl_multi_pollset_ev(multi, data, &ps, &conn->shutdown_poll);
 
-  return CURLE_OK;
+  if(!mresult) /* Remember for next time */
+    memcpy(&conn->shutdown_poll, &ps, sizeof(ps));
+  return mresult;
 }
 
 void Curl_conncache_multi_socket(struct Curl_multi *multi,
index 7ade4532d819bea65ce050111902d2a63d4a7e1b..014401cf938ebce280353f50f90442a2bc63bf9a 100644 (file)
@@ -2914,34 +2914,48 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
                               struct Curl_easy *data)
 {
   struct easy_pollset cur_poll;
+  CURLMcode mresult;
+
+  /* Fill in the 'current' struct with the state as it is now: what sockets to
+     supervise and for what actions */
+  multi_getsock(data, &cur_poll);
+  mresult = Curl_multi_pollset_ev(multi, data, &cur_poll, &data->last_poll);
+
+  if(!mresult) /* Remember for next time */
+    memcpy(&data->last_poll, &cur_poll, sizeof(cur_poll));
+  return mresult;
+}
+
+CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi,
+                                struct Curl_easy *data,
+                                struct easy_pollset *ps,
+                                struct easy_pollset *last_ps)
+{
   unsigned int i;
   struct Curl_sh_entry *entry;
   curl_socket_t s;
   int rc;
 
-  /* Fill in the 'current' struct with the state as it is now: what sockets to
-     supervise and for what actions */
-  multi_getsock(data, &cur_poll);
   /* We have 0 .. N sockets already and we get to know about the 0 .. M
      sockets we should have from now on. Detect the differences, remove no
      longer supervised ones and add new ones */
 
   /* walk over the sockets we got right now */
-  for(i = 0; i < cur_poll.num; i++) {
-    unsigned char cur_action = cur_poll.actions[i];
+  for(i = 0; i < ps->num; i++) {
+    unsigned char cur_action = ps->actions[i];
     unsigned char last_action = 0;
     int comboaction;
 
-    s = cur_poll.sockets[i];
+    s = ps->sockets[i];
 
     /* get it from the hash */
     entry = sh_getentry(&multi->sockhash, s);
     if(entry) {
       /* check if new for this transfer */
       unsigned int j;
-      for(j = 0; j< data->last_poll.num; j++) {
-        if(s == data->last_poll.sockets[j]) {
-          last_action = data->last_poll.actions[j];
+      for(j = 0; j< last_ps->num; j++) {
+        if(s == last_ps->sockets[j]) {
+          last_action = last_ps->actions[j];
           break;
         }
       }
@@ -2964,14 +2978,15 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
       if(cur_action & CURL_POLL_OUT)
         entry->writers++;
     }
-    else if(!last_action) {
+    else if(!last_action &&
+            !Curl_hash_pick(&entry->transfers, (char *)&data, /* hash key */
+                            sizeof(struct Curl_easy *))) {
       /* a new transfer using this socket */
       entry->users++;
       if(cur_action & CURL_POLL_IN)
         entry->readers++;
       if(cur_action & CURL_POLL_OUT)
         entry->writers++;
-
       /* add 'data' to the transfer hash on this socket! */
       if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */
                         sizeof(struct Curl_easy *), data)) {
@@ -3004,15 +3019,15 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
     entry->action = (unsigned int)comboaction;
   }
 
-  /* Check for last_poll.sockets that no longer appear in cur_poll.sockets.
+  /* Check for last_poll.sockets that no longer appear in ps->sockets.
    * Need to remove the easy handle from the multi->sockhash->transfers and
    * remove multi->sockhash entry when this was the last transfer */
-  for(i = 0; i< data->last_poll.num; i++) {
+  for(i = 0; i < last_ps->num; i++) {
     unsigned int j;
     bool stillused = FALSE;
-    s = data->last_poll.sockets[i];
-    for(j = 0; j < cur_poll.num; j++) {
-      if(s == cur_poll.sockets[j]) {
+    s = last_ps->sockets[i];
+    for(j = 0; j < ps->num; j++) {
+      if(s == ps->sockets[j]) {
         /* this is still supervised */
         stillused = TRUE;
         break;
@@ -3025,7 +3040,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
     /* if this is NULL here, the socket has been closed and notified so
        already by Curl_multi_closed() */
     if(entry) {
-      unsigned char oldactions = data->last_poll.actions[i];
+      unsigned char oldactions = last_ps->actions[i];
       /* this socket has been removed. Decrease user count */
       entry->users--;
       if(oldactions & CURL_POLL_OUT)
@@ -3055,8 +3070,6 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
     }
   } /* for loop over num */
 
-  /* Remember for next time */
-  memcpy(&data->last_poll, &cur_poll, sizeof(data->last_poll));
   return CURLM_OK;
 }
 
index b6eba2d54a01088414ea1a4b4f977b4cafedde3a..d3c12baee56f396dc17e9ba9db3b53188fe13733 100644 (file)
@@ -84,6 +84,15 @@ void Curl_multiuse_state(struct Curl_easy *data,
 
 void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s);
 
+/* Compare the two pollsets to notify the multi_socket API of changes
+ * in socket polling, e.g calling multi->socket_cb() with the changes if
+ * differences are seen.
+ */
+CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi,
+                                struct Curl_easy *data,
+                                struct easy_pollset *ps,
+                                struct easy_pollset *last_ps);
+
 /*
  * Add a handle and move it into PERFORM state at once. For pushed streams.
  */
index 74b744c7157e97ab2f0c118d1162d3fbeadd8329..f34aa591caad034266799bf7ab07f99bdf772e30 100644 (file)
@@ -854,6 +854,9 @@ struct connectdata {
     struct curltime start[2]; /* when filter shutdown started */
     unsigned int timeout_ms; /* 0 means no timeout */
   } shutdown;
+  /* Last pollset used in connection shutdown. Used to detect changes
+   * for multi_socket API. */
+  struct easy_pollset shutdown_poll;
 
   struct ssl_primary_config ssl_config;
 #ifndef CURL_DISABLE_PROXY