Skip to content
multi.c 73.6 KiB
Newer Older
  size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
  if (conn->server_supports_pipelining || pipeLen == 0) {
    struct curl_llist_element *curr = conn->pend_pipe->head;
    const size_t maxPipeLen =
      conn->server_supports_pipelining ? MAX_PIPELINE_LENGTH : 1;
    while(pipeLen < maxPipeLen && curr) {
      Curl_llist_move(conn->pend_pipe, curr,
                      conn->send_pipe, conn->send_pipe->tail);
      Curl_pgrsTime(curr->ptr, TIMER_PRETRANSFER);
      ++result; /* count how many handles we moved */
      curr = conn->pend_pipe->head;
      ++pipeLen;
    }
  }

    /* something moved, check for a new send pipeline leader */
    if(sendhead != conn->send_pipe->head) {
      /* this is a new one as head, expire it */
      conn->writechannel_inuse = FALSE; /* not in use yet */
      infof(conn->data, "%p is at send pipe head!\n",
            conn->send_pipe->head->ptr);
      Curl_expire(conn->send_pipe->head->ptr, 1);
    }
  }

/* Move this transfer from the sending list to the receiving list.

   Pay special attention to the new sending list "leader" as it needs to get
   checked to update what sockets it acts on.

 */
static void moveHandleFromSendToRecvPipeline(struct SessionHandle *handle,
                                            struct connectdata *conn)
{
  struct curl_llist_element *curr;

  curr = conn->send_pipe->head;
  while(curr) {
    if(curr->ptr == handle) {
      Curl_llist_move(conn->send_pipe, curr,
                      conn->recv_pipe, conn->recv_pipe->tail);

      if(conn->send_pipe->head) {
        /* Since there's a new easy handle at the start of the send pipeline,
           set its timeout value to 1ms to make it trigger instantly */
        conn->writechannel_inuse = FALSE; /* not used now */
        infof(conn->data, "%p is at send pipe head B!\n",
              conn->send_pipe->head->ptr);
        Curl_expire(conn->send_pipe->head->ptr, 1);
      }

      /* The receiver's list is not really interesting here since either this
         handle is now first in the list and we'll deal with it soon, or
         another handle is already first and thus is already taken care of */

      break; /* we're done! */
    }
    curr = curr->next;
  }
}

static bool isHandleAtHead(struct SessionHandle *handle,
                           struct curl_llist *pipeline)
{
  struct curl_llist_element *curr = pipeline->head;
  if(curr)
    return (bool)(curr->ptr == handle);

  return FALSE;
}

/* 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() */
void Curl_expire(struct SessionHandle *data, long milli)
{
  struct Curl_multi *multi = data->multi;
  struct timeval *nowp = &data->state.expiretime;

  /* this is only interesting for multi-interface using libcurl, and only
     while there is still a multi interface 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 */
      rc = Curl_splayremovebyaddr(multi->timetree,
                                  &data->state.timenode,
                                  &multi->timetree);
      if(rc)
        infof(data, "Internal error clearing splay node = %d\n", rc);
Yang Tse's avatar
Yang Tse committed
      nowp->tv_sec = 0;
Yang Tse's avatar
Yang Tse committed
      nowp->tv_usec = 0;
    }
  }
  else {
    struct timeval set;
    int rest;

    set = Curl_tvnow();
    set.tv_sec += milli/1000;
    set.tv_usec += (milli%1000)*1000;

    rest = (int)(set.tv_usec - 1000000);
    if(rest > 0) {
      /* bigger than a full microsec */
      set.tv_sec++;
      set.tv_usec -= 1000000;
    }

    if(nowp->tv_sec || nowp->tv_usec) {
      /* This means that the struct is added as a node in the splay tree.
         Compare if the new time is earlier, and only remove-old/add-new if it
         is. */
      long diff = curlx_tvdiff(set, *nowp);
      if(diff > 0)
        /* the new expire time was later so we don't change this */
        return;

      /* Since this is an updated time, we must remove the previous entry from
         the splay tree first and then re-add the new value */
      rc = Curl_splayremovebyaddr(multi->timetree,
                                  &data->state.timenode,
                                  &multi->timetree);
      if(rc)
        infof(data, "Internal error removing splay node = %d\n", rc);
    infof(data, "Expire at %ld / %ld (%ldms) %p\n",
          (long)nowp->tv_sec, (long)nowp->tv_usec, milli, data);
    multi->timetree = Curl_splayinsert(*nowp,
                                       multi->timetree,
                                       &data->state.timenode);
  }
#if 0
  Curl_splayprint(multi->timetree, 0, TRUE);
#endif
}

CURLMcode curl_multi_assign(CURLM *multi_handle,
                            curl_socket_t s, void *hashp)
{
  struct Curl_sh_entry *there = NULL;
  struct Curl_multi *multi = (struct Curl_multi *)multi_handle;

  if(s != CURL_SOCKET_BAD)
    there = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(curl_socket_t));

  if(!there)
    return CURLM_BAD_SOCKET;

  there->socketp = hashp;

  return CURLM_OK;
}

static bool multi_conn_using(struct Curl_multi *multi,
                             struct SessionHandle *data)
{
  /* any live CLOSEACTION-connections pointing to the give 'data' ? */
  int i;

  for(i=0; i< multi->connc->num; i++) {
    if(multi->connc->connects[i] &&
       (multi->connc->connects[i]->data == data) &&
       multi->connc->connects[i]->protocol & PROT_CLOSEACTION)
      return TRUE;
  }

  return FALSE;
}

/* Add the given data pointer to the list of 'closure handles' that are kept
   around only to be able to close some connections nicely - just make sure
   that this handle isn't already added, like for the cases when an easy
   handle is removed, added and removed again... */
static void add_closure(struct Curl_multi *multi,
                        struct SessionHandle *data)
  struct closure *cl = calloc(sizeof(struct closure), 1);
  struct closure *p=NULL;
  struct closure *n;
  if(cl) {
    cl->easy_handle = data;
    cl->next = multi->closure;
    multi->closure = cl;
  }

  p = multi->closure;
  cl = p->next; /* start immediately on the second since the first is the one
                   we just added and it is _very_ likely to actually exist
                   used in the cache since that's the whole purpose of adding
                   it to this list! */

  /* When adding, scan through all the other currently kept handles and see if
     there are any connections still referring to them and kill them if not. */
  while(cl) {
    bool inuse = FALSE;
    for(i=0; i< multi->connc->num; i++) {
      if(multi->connc->connects[i] &&
         (multi->connc->connects[i]->data == cl->easy_handle)) {
        inuse = TRUE;
        break;
      }
    }

    n = cl->next;

    if(!inuse) {
      /* cl->easy_handle is now killable */
      infof(data, "Delayed kill of easy handle %p\n", cl->easy_handle);
      /* unmark it as not having a connection around that uses it anymore */
      cl->easy_handle->state.shared_conn= NULL;
      Curl_close(cl->easy_handle);
      if(p)
        p->next = n;
      else
        multi->closure = n;
      free(cl);
    }
    else
      p = cl;

    cl = n;
  }

}
void Curl_multi_dump(const struct Curl_multi *multi_handle)
{
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
  struct Curl_one_easy *easy;
  int i;
  fprintf(stderr, "* Multi status: %d handles, %d alive\n",
          multi->num_easy, multi->num_alive);
  for(easy=multi->easy.next; easy != &multi->easy; easy = easy->next) {
    if(easy->state != CURLM_STATE_COMPLETED) {
      /* only display handles that are not completed */
      fprintf(stderr, "handle %p, state %s, %d sockets\n",
              (void *)easy->easy_handle,
              statename[easy->state], easy->numsocks);
      for(i=0; i < easy->numsocks; i++) {
        curl_socket_t s = easy->sockets[i];
        struct Curl_sh_entry *entry =
          Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));

        fprintf(stderr, "%d ", (int)s);
        if(!entry) {
          fprintf(stderr, "INTERNAL CONFUSION\n");
          continue;
        }
        fprintf(stderr, "[%s %s] ",
                entry->action&CURL_POLL_IN?"RECVING":"",
                entry->action&CURL_POLL_OUT?"SENDING":"");
      }
      if(easy->numsocks)
        fprintf(stderr, "\n");
    }
  }
}
#endif