Commit 7de44e0a authored by Ben Greear's avatar Ben Greear Committed by Daniel Stenberg
Browse files

Make rate-limitation logic smoother

This gives a smoother rate limitation performance by using
sub-second pauses and also taking the buffer sizes into
account.
parent 05de2cf1
Loading
Loading
Loading
Loading
+6 −36
Original line number Diff line number Diff line
@@ -267,7 +267,7 @@ static CURLcode tftp_set_timeouts(tftp_state_data_t *state)
    state->retry_max=50;

  /* Compute the re-ACK interval to suit the timeout */
  state->retry_time = timeout/state->retry_max;
  state->retry_time = (int)(timeout/state->retry_max);
  if(state->retry_time<1)
    state->retry_time=1;

@@ -1177,36 +1177,6 @@ static long tftp_state_timeout(struct connectdata *conn, tftp_event_t *event)
  }
}

static long sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
                       int pkt_size)
{
  curl_off_t min_sleep = 0;
  curl_off_t rv = 0;

  if (rate_bps == 0)
    return 0;

  if (cur_rate_bps > (rate_bps + (rate_bps >> 10))) {
    /* running too fast */
    rate_bps -= rate_bps >> 6;
    min_sleep = 1;
  }
  else if (cur_rate_bps < (rate_bps - (rate_bps >> 10))) {
    /* running too slow */
    rate_bps += rate_bps >> 6;
  }

  rv = ((curl_off_t)((pkt_size * 8) * 1000) / rate_bps);

  if (rv < min_sleep)
    rv = min_sleep;

  if(rv > 0x7fffffff)
    rv = 0x7fffffff;

  return (long)rv;
}


/**********************************************************
 *
@@ -1239,7 +1209,7 @@ static CURLcode tftp_easy_statemach(struct connectdata *conn)
      if (data->set.max_send_speed &&
          (data->progress.ulspeed > data->set.max_send_speed)) {
        fd_read = CURL_SOCKET_BAD;
        timeout_ms = sleep_time(data->set.max_send_speed,
        timeout_ms = Curl_sleep_time(data->set.max_send_speed,
                                     data->progress.ulspeed, state->blksize);
      }
      else {
@@ -1250,7 +1220,7 @@ static CURLcode tftp_easy_statemach(struct connectdata *conn)
      if (data->set.max_recv_speed &&
          (data->progress.dlspeed > data->set.max_recv_speed)) {
        fd_read = CURL_SOCKET_BAD;
        timeout_ms = sleep_time(data->set.max_recv_speed,
        timeout_ms = Curl_sleep_time(data->set.max_recv_speed,
                                     data->progress.dlspeed, state->blksize);
      }
      else {
@@ -1268,7 +1238,7 @@ static CURLcode tftp_easy_statemach(struct connectdata *conn)


    /* Wait until ready to read or timeout occurs */
    rc=Curl_socket_ready(fd_read, CURL_SOCKET_BAD, timeout_ms);
    rc = Curl_socket_ready(fd_read, CURL_SOCKET_BAD, (int)(timeout_ms));

    k->now = Curl_tvnow();

+83 −5
Original line number Diff line number Diff line
@@ -1175,6 +1175,60 @@ int Curl_single_getsock(const struct connectdata *conn,
  return bitmap;
}

/*
 * Determine optimum sleep time based on configured rate, current rate,
 * and packet size.
 * Returns value in mili-seconds.
 *
 * The basic idea is to adjust the desired rate up/down in this method
 * based on whether we are running too slow or too fast.  Then, calculate
 * how many miliseconds to wait for the next packet to achieve this new
 * rate.
 */
long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
                             int pkt_size)
{
  curl_off_t min_sleep = 0;
  curl_off_t rv = 0;

  if (rate_bps == 0)
    return 0;

  /* If running faster than about .1% of the desired speed, slow
   * us down a bit.  Use shift instead of division as the 0.1%
   * cutoff is arbitrary anyway.
   */
  if (cur_rate_bps > (rate_bps + (rate_bps >> 10))) {
    /* running too fast, decrease target rate by 1/64th of rate */
    rate_bps -= rate_bps >> 6;
    min_sleep = 1;
  }
  else if (cur_rate_bps < (rate_bps - (rate_bps >> 10))) {
    /* running too slow, increase target rate by 1/64th of rate */
    rate_bps += rate_bps >> 6;
  }

  /* Determine number of miliseconds to wait until we do
   * the next packet at the adjusted rate.  We should wait
   * longer when using larger packets, for instance.
   */
  rv = ((curl_off_t)((pkt_size * 8) * 1000) / rate_bps);

  /* Catch rounding errors and always slow down at least 1ms if
   * we are running too fast.
   */
  if (rv < min_sleep)
    rv = min_sleep;

  /* Bound value to fit in 'long' on 32-bit platform.  That's
   * plenty long enough anyway!
   */
  if(rv > 0x7fffffff)
    rv = 0x7fffffff;
  
  return (long)rv;
}


/*
 * Transfer()
@@ -1197,6 +1251,8 @@ Transfer(struct connectdata *conn)
  bool done=FALSE;
  bool first=TRUE;
  int timeout_ms;
  int buffersize;
  int totmp;

  if((conn->sockfd == CURL_SOCKET_BAD) &&
     (conn->writesockfd == CURL_SOCKET_BAD))
@@ -1211,6 +1267,7 @@ Transfer(struct connectdata *conn)
    curl_socket_t fd_read = conn->sockfd;
    curl_socket_t fd_write = conn->writesockfd;
    int keepon = k->keepon;
    timeout_ms = 1000;

    if(conn->waitfor) {
      /* if waitfor is set, get the RECV and SEND bits from that but keep the
@@ -1228,6 +1285,16 @@ Transfer(struct connectdata *conn)
      k->keepon &= ~KEEP_SEND_HOLD;
    }
    else {
      if (data->set.upload && data->set.max_send_speed &&
          (data->progress.ulspeed > data->set.max_send_speed) ) {
        /* calculate upload rate-limitation timeout. */
        buffersize = (int)(data->set.buffer_size ?
                           data->set.buffer_size : BUFSIZE);
        totmp = (int)Curl_sleep_time(data->set.max_send_speed,
                                     data->progress.ulspeed, buffersize);
        if (totmp < timeout_ms)
          timeout_ms = totmp;
      }
      fd_write = CURL_SOCKET_BAD;
      if(keepon & KEEP_SEND)
        k->keepon |= KEEP_SEND_HOLD; /* hold it */
@@ -1239,6 +1306,16 @@ Transfer(struct connectdata *conn)
      k->keepon &= ~KEEP_RECV_HOLD;
    }
    else {
      if ((!data->set.upload) && data->set.max_recv_speed &&
          (data->progress.dlspeed > data->set.max_recv_speed)) {
        /* Calculate download rate-limitation timeout. */
        buffersize = (int)(data->set.buffer_size ?
                           data->set.buffer_size : BUFSIZE);
        totmp = (int)Curl_sleep_time(data->set.max_recv_speed,
                                     data->progress.dlspeed, buffersize);
        if (totmp < timeout_ms)
          timeout_ms = totmp;
      }
      fd_read = CURL_SOCKET_BAD;
      if(keepon & KEEP_RECV)
        k->keepon |= KEEP_RECV_HOLD; /* hold it */
@@ -1267,14 +1344,15 @@ Transfer(struct connectdata *conn)
      timeout_ms = 0;
    else {
      if(data->set.timeout) {
        timeout_ms = data->set.timeout - Curl_tvdiff(k->now, k->start);
        if(timeout_ms > 1000)
          timeout_ms = 1000;
        else if(timeout_ms < 0)
        totmp = (int)(data->set.timeout - Curl_tvdiff(k->now, k->start));
        if(totmp < 0)
          return CURLE_OPERATION_TIMEDOUT;
      }
      else
        timeout_ms = 1000;
        totmp = 1000;

      if (totmp < timeout_ms)
        timeout_ms = totmp;
    }

    switch (Curl_socket_ready(fd_read, fd_write, timeout_ms)) {
+4 −0
Original line number Diff line number Diff line
@@ -60,4 +60,8 @@ Curl_setup_transfer (struct connectdata *data,
                                           -1 disables */
               curl_off_t *writecountp /* return number of bytes written */
);

long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps,
                     int pkt_size);

#endif