Commit bde2f09d authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

multi: make Curl_expire() work with 0 ms timeouts

Previously, passing a timeout of zero to Curl_expire() was a magic code
for clearing all timeouts for the handle. That is now instead made with
the new Curl_expire_clear() function and thus a 0 timeout is fine to set
and will trigger a timeout ASAP.

This will help removing short delays, in particular notable when doing
HTTP/2.
parent 6eb60c2d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1044,7 +1044,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
  if(!result &&
     ((newstate&(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
      (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) )
    Curl_expire(data, 1); /* get this handle going again */
    Curl_expire(data, 0); /* get this handle going again */

  return result;
}
+4 −5
Original line number Diff line number Diff line
@@ -547,7 +547,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,

      /* if we receive data for another handle, wake that up */
      if(conn_s->data != data_s)
        Curl_expire(data_s, 1);
        Curl_expire(data_s, 0);
    }
    break;
  case NGHTTP2_PUSH_PROMISE:
@@ -621,8 +621,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,

  /* if we receive data for another handle, wake that up */
  if(conn->data != data_s)
    Curl_expire(data_s, 1); /* TODO: fix so that this can be set to 0 for
                               immediately? */
    Curl_expire(data_s, 0);

  DEBUGF(infof(data_s, "%zu data received for stream %u "
               "(%zu left in buffer %p, total %zu)\n",
@@ -883,7 +882,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
    Curl_add_buffer(stream->header_recvbuf, " \r\n", 3);
    /* if we receive data for another handle, wake that up */
    if(conn->data != data_s)
      Curl_expire(data_s, 1);
      Curl_expire(data_s, 0);

    DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
                 stream->status_code, data_s));
@@ -899,7 +898,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
  Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
  /* if we receive data for another handle, wake that up */
  if(conn->data != data_s)
    Curl_expire(data_s, 1);
    Curl_expire(data_s, 0);

  DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
               value));
+84 −74
Original line number Diff line number Diff line
@@ -442,7 +442,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
     sockets that time-out or have actions will be dealt with. Since this
     handle has no action yet, we make sure it times out to get things to
     happen. */
  Curl_expire(data, 1);
  Curl_expire(data, 0);

  /* increase the node-counter */
  multi->num_easy++;
@@ -695,7 +695,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
  /* The timer must be shut down before data->multi is set to NULL,
     else the timenode will remain in the splay tree after
     curl_easy_cleanup is called. */
  Curl_expire(data, 0);
  Curl_expire_clear(data);

  if(data->dns.hostcachetype == HCACHE_MULTI) {
    /* stop using the multi handle's DNS cache */
@@ -1901,7 +1901,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,

        /* expire the new receiving pipeline head */
        if(data->easy_conn->recv_pipe->head)
          Curl_expire_latest(data->easy_conn->recv_pipe->head->ptr, 1);
          Curl_expire_latest(data->easy_conn->recv_pipe->head->ptr, 0);

        /* Check if we can move pending requests to send pipe */
        Curl_multi_process_pending_handles(multi);
@@ -2011,7 +2011,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         that could be freed anytime */
      data->easy_conn = NULL;

      Curl_expire(data, 0); /* stop all timers */
      Curl_expire_clear(data); /* stop all timers */
      break;

    case CURLM_STATE_MSGSENT:
@@ -2879,49 +2879,21 @@ multi_addtimeout(struct curl_llist *timeoutlist,
 * given a number of milliseconds from now to use to set the 'act before
 * this'-time for the transfer, to be extracted by curl_multi_timeout()
 *
 * Note that the timeout will be added to a queue of timeouts if it defines a
 * moment in time that is later than the current head of queue.
 *
 * Pass zero to clear all timeout values for this handle.
 * The timeout will be added to a queue of timeouts if it defines a moment in
 * time that is later than the current head of queue.
 */
void Curl_expire(struct Curl_easy *data, long milli)
{
  struct Curl_multi *multi = data->multi;
  struct timeval *nowp = &data->state.expiretime;
  int rc;
  struct timeval set;

  /* this is only interesting while there is still an associated multi struct
     remaining! */
  if(!multi)
    return;

  if(!milli) {
    /* No timeout, clear the time data. */
    if(nowp->tv_sec || nowp->tv_usec) {
      /* Since this is an cleared time, we must remove the previous entry from
         the splay tree */
      struct curl_llist *list = data->state.timeoutlist;

      rc = Curl_splayremovebyaddr(multi->timetree,
                                  &data->state.timenode,
                                  &multi->timetree);
      if(rc)
        infof(data, "Internal error clearing splay node = %d\n", rc);

      /* flush the timeout list too */
      while(list->size > 0)
        Curl_llist_remove(list, list->tail, NULL);

#ifdef DEBUGBUILD
      infof(data, "Expire cleared\n");
#endif
      nowp->tv_sec = 0;
      nowp->tv_usec = 0;
    }
  }
  else {
    struct timeval set;

  set = Curl_tvnow();
  set.tv_sec += milli/1000;
  set.tv_usec += (milli%1000)*1000;
@@ -2958,14 +2930,9 @@ void Curl_expire(struct Curl_easy *data, long milli)

  *nowp = set;
  data->state.timenode.payload = data;
    multi->timetree = Curl_splayinsert(*nowp,
                                       multi->timetree,
  multi->timetree = Curl_splayinsert(*nowp, multi->timetree,
                                     &data->state.timenode);
}
#if 0
  Curl_splayprint(multi->timetree, 0, TRUE);
#endif
}

/*
 * Curl_expire_latest()
@@ -3007,6 +2974,49 @@ void Curl_expire_latest(struct Curl_easy *data, long milli)
  Curl_expire(data, milli);
}


/*
 * Curl_expire_clear()
 *
 * Clear ALL timeout values for this handle.
 */
void Curl_expire_clear(struct Curl_easy *data)
{
  struct Curl_multi *multi = data->multi;
  struct timeval *nowp = &data->state.expiretime;
  int rc;

  /* this is only interesting while there is still an associated multi struct
     remaining! */
  if(!multi)
    return;

  if(nowp->tv_sec || nowp->tv_usec) {
    /* Since this is an cleared time, we must remove the previous entry from
       the splay tree */
    struct curl_llist *list = data->state.timeoutlist;

    rc = Curl_splayremovebyaddr(multi->timetree,
                                &data->state.timenode,
                                &multi->timetree);
    if(rc)
      infof(data, "Internal error clearing splay node = %d\n", rc);

    /* flush the timeout list too */
    while(list->size > 0)
      Curl_llist_remove(list, list->tail, NULL);

#ifdef DEBUGBUILD
    infof(data, "Expire cleared\n");
#endif
    nowp->tv_sec = 0;
    nowp->tv_usec = 0;
  }
}




CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s,
                            void *hashp)
{
@@ -3067,7 +3077,7 @@ void Curl_multi_process_pending_handles(struct Curl_multi *multi)
      Curl_llist_remove(multi->pending, e, NULL);

      /* Make sure that the handle will be processed soonish. */
      Curl_expire_latest(data, 1);
      Curl_expire_latest(data, 0);
    }

    e = next; /* operate on next handle */
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
 * Prototypes for library-wide functions provided by multi.c
 */
void Curl_expire(struct Curl_easy *data, long milli);
void Curl_expire_clear(struct Curl_easy *data);
void Curl_expire_latest(struct Curl_easy *data, long milli);
bool Curl_pipeline_wanted(const struct Curl_multi* multi, int bits);
void Curl_multi_handlePipeBreak(struct Curl_easy *data);
+3 −3
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se>
 * Copyright (C) 2013-2015, Daniel Stenberg, <daniel@haxx.se>, et al.
 * Copyright (C) 2013-2016, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
@@ -114,7 +114,7 @@ CURLcode Curl_add_handle_to_pipeline(struct Curl_easy *handle,
  if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) {
    /* this is a new one as head, expire it */
    Curl_pipeline_leave_write(conn); /* not in use yet */
    Curl_expire(conn->send_pipe->head->ptr, 1);
    Curl_expire(conn->send_pipe->head->ptr, 0);
  }

#if 0 /* enable for pipeline debugging */
@@ -149,7 +149,7 @@ void Curl_move_handle_from_send_to_recv_pipe(struct Curl_easy *handle,
        infof(conn->data, "%p is at send pipe head B!\n",
              (void *)conn->send_pipe->head->ptr);
#endif
        Curl_expire(conn->send_pipe->head->ptr, 1);
        Curl_expire(conn->send_pipe->head->ptr, 0);
      }

      /* The receiver's list is not really interesting here since either this
Loading