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 Original line Diff line number Diff line
@@ -1044,7 +1044,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
  if(!result &&
  if(!result &&
     ((newstate&(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
     ((newstate&(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
      (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;
  return result;
}
}
+4 −5
Original line number Original line 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 we receive data for another handle, wake that up */
      if(conn_s->data != data_s)
      if(conn_s->data != data_s)
        Curl_expire(data_s, 1);
        Curl_expire(data_s, 0);
    }
    }
    break;
    break;
  case NGHTTP2_PUSH_PROMISE:
  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 we receive data for another handle, wake that up */
  if(conn->data != data_s)
  if(conn->data != data_s)
    Curl_expire(data_s, 1); /* TODO: fix so that this can be set to 0 for
    Curl_expire(data_s, 0);
                               immediately? */


  DEBUGF(infof(data_s, "%zu data received for stream %u "
  DEBUGF(infof(data_s, "%zu data received for stream %u "
               "(%zu left in buffer %p, total %zu)\n",
               "(%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);
    Curl_add_buffer(stream->header_recvbuf, " \r\n", 3);
    /* if we receive data for another handle, wake that up */
    /* if we receive data for another handle, wake that up */
    if(conn->data != data_s)
    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",
    DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
                 stream->status_code, data_s));
                 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);
  Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
  /* if we receive data for another handle, wake that up */
  /* if we receive data for another handle, wake that up */
  if(conn->data != data_s)
  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,
  DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
               value));
               value));
+84 −74
Original line number Original line 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
     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
     handle has no action yet, we make sure it times out to get things to
     happen. */
     happen. */
  Curl_expire(data, 1);
  Curl_expire(data, 0);


  /* increase the node-counter */
  /* increase the node-counter */
  multi->num_easy++;
  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,
  /* The timer must be shut down before data->multi is set to NULL,
     else the timenode will remain in the splay tree after
     else the timenode will remain in the splay tree after
     curl_easy_cleanup is called. */
     curl_easy_cleanup is called. */
  Curl_expire(data, 0);
  Curl_expire_clear(data);


  if(data->dns.hostcachetype == HCACHE_MULTI) {
  if(data->dns.hostcachetype == HCACHE_MULTI) {
    /* stop using the multi handle's DNS cache */
    /* 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 */
        /* expire the new receiving pipeline head */
        if(data->easy_conn->recv_pipe->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 */
        /* Check if we can move pending requests to send pipe */
        Curl_multi_process_pending_handles(multi);
        Curl_multi_process_pending_handles(multi);
@@ -2011,7 +2011,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         that could be freed anytime */
         that could be freed anytime */
      data->easy_conn = NULL;
      data->easy_conn = NULL;


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


    case CURLM_STATE_MSGSENT:
    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
 * 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()
 * 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
 * The timeout will be added to a queue of timeouts if it defines a moment in
 * moment in time that is later than the current head of queue.
 * time that is later than the current head of queue.
 *
 * Pass zero to clear all timeout values for this handle.
 */
 */
void Curl_expire(struct Curl_easy *data, long milli)
void Curl_expire(struct Curl_easy *data, long milli)
{
{
  struct Curl_multi *multi = data->multi;
  struct Curl_multi *multi = data->multi;
  struct timeval *nowp = &data->state.expiretime;
  struct timeval *nowp = &data->state.expiretime;
  int rc;
  int rc;
  struct timeval set;


  /* this is only interesting while there is still an associated multi struct
  /* this is only interesting while there is still an associated multi struct
     remaining! */
     remaining! */
  if(!multi)
  if(!multi)
    return;
    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 = Curl_tvnow();
  set.tv_sec += milli/1000;
  set.tv_sec += milli/1000;
  set.tv_usec += (milli%1000)*1000;
  set.tv_usec += (milli%1000)*1000;
@@ -2958,14 +2930,9 @@ void Curl_expire(struct Curl_easy *data, long milli)


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


/*
/*
 * Curl_expire_latest()
 * Curl_expire_latest()
@@ -3007,6 +2974,49 @@ void Curl_expire_latest(struct Curl_easy *data, long milli)
  Curl_expire(data, 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,
CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s,
                            void *hashp)
                            void *hashp)
{
{
@@ -3067,7 +3077,7 @@ void Curl_multi_process_pending_handles(struct Curl_multi *multi)
      Curl_llist_remove(multi->pending, e, NULL);
      Curl_llist_remove(multi->pending, e, NULL);


      /* Make sure that the handle will be processed soonish. */
      /* 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 */
    e = next; /* operate on next handle */
+1 −0
Original line number Original line Diff line number Diff line
@@ -26,6 +26,7 @@
 * Prototypes for library-wide functions provided by multi.c
 * Prototypes for library-wide functions provided by multi.c
 */
 */
void Curl_expire(struct Curl_easy *data, long milli);
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);
void Curl_expire_latest(struct Curl_easy *data, long milli);
bool Curl_pipeline_wanted(const struct Curl_multi* multi, int bits);
bool Curl_pipeline_wanted(const struct Curl_multi* multi, int bits);
void Curl_multi_handlePipeBreak(struct Curl_easy *data);
void Curl_multi_handlePipeBreak(struct Curl_easy *data);
+3 −3
Original line number Original line Diff line number Diff line
@@ -6,7 +6,7 @@
 *                             \___|\___/|_| \_\_____|
 *                             \___|\___/|_| \_\_____|
 *
 *
 * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se>
 * 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
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * 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) {
  if(pipeline == conn->send_pipe && sendhead != conn->send_pipe->head) {
    /* this is a new one as head, expire it */
    /* this is a new one as head, expire it */
    Curl_pipeline_leave_write(conn); /* not in use yet */
    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 */
#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",
        infof(conn->data, "%p is at send pipe head B!\n",
              (void *)conn->send_pipe->head->ptr);
              (void *)conn->send_pipe->head->ptr);
#endif
#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
      /* The receiver's list is not really interesting here since either this
Loading