~ubuntu-branches/ubuntu/quantal/curl/quantal-updates

« back to all changes in this revision

Viewing changes to lib/multi.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Schuldei
  • Date: 2009-04-02 23:35:45 UTC
  • mto: (1.2.1 upstream) (3.2.3 sid)
  • mto: This revision was merged to the branch mainline in revision 38.
  • Revision ID: james.westby@ubuntu.com-20090402233545-geixkwhe3izccjt7
Tags: upstream-7.19.4
ImportĀ upstreamĀ versionĀ 7.19.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
 *                            | (__| |_| |  _ <| |___
6
6
 *                             \___|\___/|_| \_\_____|
7
7
 *
8
 
 * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
 
8
 * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
9
9
 *
10
10
 * This software is licensed as described in the file COPYING, which
11
11
 * you should have received as part of this distribution. The terms
18
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
19
 * KIND, either express or implied.
20
20
 *
21
 
 * $Id: multi.c,v 1.172 2008-05-28 20:56:19 bagder Exp $
 
21
 * $Id: multi.c,v 1.195 2009-02-20 08:16:04 bagder Exp $
22
22
 ***************************************************************************/
23
23
 
24
24
#include "setup.h"
37
37
#include "url.h"
38
38
#include "connect.h"
39
39
#include "progress.h"
40
 
#include "memory.h"
41
40
#include "easyif.h"
42
41
#include "multiif.h"
43
42
#include "sendf.h"
44
43
#include "timeval.h"
45
44
#include "http.h"
46
45
 
 
46
#define _MPRINTF_REPLACE /* use our functions only */
 
47
#include <curl/mprintf.h>
 
48
 
 
49
#include "memory.h"
47
50
/* The last #include file should be: */
48
51
#include "memdebug.h"
49
52
 
213
216
  "DONE",
214
217
  "COMPLETED",
215
218
};
216
 
 
217
 
void curl_multi_dump(CURLM *multi_handle);
218
219
#endif
219
220
 
220
221
/* always use this function to change state, to make debugging easier */
314
315
{
315
316
  struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis;
316
317
 
317
 
  free(p);
 
318
  if(p)
 
319
    free(p);
318
320
}
319
321
 
320
322
static size_t fd_key_compare(void*k1, size_t k1_len, void*k2, size_t k2_len)
321
323
{
322
324
  (void) k1_len; (void) k2_len;
323
325
 
324
 
  return ((*((int* ) k1)) == (*((int* ) k2))) ? 1 : 0;
 
326
  return (*((int* ) k1)) == (*((int* ) k2));
325
327
}
326
328
 
327
329
static size_t hash_fd(void* key, size_t key_length, size_t slots_num)
358
360
 
359
361
CURLM *curl_multi_init(void)
360
362
{
361
 
  struct Curl_multi *multi = (void *)calloc(sizeof(struct Curl_multi), 1);
 
363
  struct Curl_multi *multi = calloc(sizeof(struct Curl_multi), 1);
362
364
 
363
365
  if(!multi)
364
366
    return NULL;
419
421
    return CURLM_BAD_EASY_HANDLE;
420
422
 
421
423
  /* Now, time to add an easy handle to the multi stack */
422
 
  easy = (struct Curl_one_easy *)calloc(sizeof(struct Curl_one_easy), 1);
 
424
  easy = calloc(sizeof(struct Curl_one_easy), 1);
423
425
  if(!easy)
424
426
    return CURLM_OUT_OF_MEMORY;
425
427
 
534
536
  /* increase the alive-counter */
535
537
  multi->num_alive++;
536
538
 
 
539
  /* A somewhat crude work-around for a little glitch in update_timer() that
 
540
     happens if the lastcall time is set to the same time when the handle is
 
541
     removed as when the next handle is added, as then the check in
 
542
     update_timer() that prevents calling the application multiple times with
 
543
     the same timer infor will not trigger and then the new handle's timeout
 
544
     will not be notified to the app.
 
545
 
 
546
     The work-around is thus simply to clear the 'lastcall' variable to force
 
547
     update_timer() to always trigger a callback to the app when a new easy
 
548
     handle is added */
 
549
  memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
 
550
 
537
551
  update_timer(multi);
538
552
  return CURLM_OK;
539
553
}
573
587
 
574
588
  if(easy) {
575
589
    bool premature = (bool)(easy->state != CURLM_STATE_COMPLETED);
 
590
    bool easy_owns_conn = (bool)(easy->easy_conn &&
 
591
                                 (easy->easy_conn->data == easy->easy_handle));
576
592
 
577
593
    /* If the 'state' is not INIT or COMPLETED, we might need to do something
578
594
       nice to put the easy_handle in a good known state when this returns. */
606
622
      easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
607
623
    }
608
624
 
609
 
    /* we must call Curl_done() here (if we still "own it") so that we don't
610
 
       leave a half-baked one around */
611
 
    if(easy->easy_conn &&
612
 
       (easy->easy_conn->data == easy->easy_handle)) {
613
 
 
614
 
      /* Curl_done() clears the conn->data field to lose the association
615
 
         between the easy handle and the connection
616
 
 
617
 
         Note that this ignores the return code simply because there's nothing
618
 
         really useful to do with it anyway! */
619
 
      (void)Curl_done(&easy->easy_conn, easy->result, premature);
620
 
 
621
 
      if(easy->easy_conn)
622
 
        /* the connection is still alive, set back the association to enable
623
 
           the check below to trigger TRUE */
624
 
        easy->easy_conn->data = easy->easy_handle;
 
625
    if(easy->easy_conn) {
 
626
 
 
627
      /* we must call Curl_done() here (if we still "own it") so that we don't
 
628
         leave a half-baked one around */
 
629
      if (easy_owns_conn) {
 
630
 
 
631
        /* Curl_done() clears the conn->data field to lose the association
 
632
           between the easy handle and the connection
 
633
 
 
634
           Note that this ignores the return code simply because there's
 
635
           nothing really useful to do with it anyway! */
 
636
        (void)Curl_done(&easy->easy_conn, easy->result, premature);
 
637
 
 
638
        if(easy->easy_conn)
 
639
          /* the connection is still alive, set back the association to enable
 
640
             the check below to trigger TRUE */
 
641
          easy->easy_conn->data = easy->easy_handle;
 
642
      }
 
643
      else
 
644
        /* Clear connection pipelines, if Curl_done above was not called */
 
645
        Curl_getoff_all_pipelines(easy->easy_handle, easy->easy_conn);
625
646
    }
626
647
 
627
648
    /* If this easy_handle was the last one in charge for one or more
628
 
       connections a the shared connection cache, we might need to keep this
 
649
       connections in the shared connection cache, we might need to keep this
629
650
       handle around until either A) the connection is closed and killed
630
651
       properly, or B) another easy_handle uses the connection.
631
652
 
657
678
         to that since we're not part of that handle anymore */
658
679
      easy->easy_handle->state.connc = NULL;
659
680
 
660
 
      /* and modify the connectindex since this handle can't point to the
661
 
         connection cache anymore */
662
 
      if(easy->easy_conn &&
 
681
      /* Modify the connectindex since this handle can't point to the
 
682
         connection cache anymore.
 
683
 
 
684
         TODO: consider if this is really what we want. The connection cache
 
685
         is within the multi handle and that owns the connections so we should
 
686
         not need to touch connections like this when we just remove an easy
 
687
         handle...
 
688
      */
 
689
      if(easy->easy_conn && easy_owns_conn &&
663
690
         (easy->easy_conn->send_pipe->size +
664
691
          easy->easy_conn->recv_pipe->size == 0))
665
692
        easy->easy_conn->connectindex = -1;
759
786
     happen when this is called from curl_multi_remove_handle() =>
760
787
     singlesocket() => multi_getsock().
761
788
  */
762
 
 
763
 
  if(easy->easy_handle->state.pipe_broke ||
764
 
      !easy->easy_conn) {
 
789
  if(easy->easy_handle->state.pipe_broke || !easy->easy_conn)
765
790
    return 0;
766
 
  }
767
791
 
768
792
  if(easy->state > CURLM_STATE_CONNECT &&
769
793
      easy->state < CURLM_STATE_COMPLETED) {
871
895
  bool async;
872
896
  bool protocol_connect = FALSE;
873
897
  bool dophase_done;
874
 
  bool done;
 
898
  bool done = FALSE;
875
899
  CURLMcode result = CURLM_OK;
876
900
  struct SingleRequest *k;
877
901
 
965
989
      easy->result = Curl_is_resolved(easy->easy_conn, &dns);
966
990
 
967
991
      if(dns) {
 
992
        /* Update sockets here. Mainly because the socket(s) may have been
 
993
           closed and the application thus needs to be told, even if it is
 
994
           likely that the same socket(s) will again be used further down. */
 
995
        singlesocket(multi, easy);
 
996
 
968
997
        /* Perform the next step in the connection phase, and then move on
969
998
           to the WAITCONNECT state */
970
999
        easy->result = Curl_async_resolved(easy->easy_conn,
1004
1033
      /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
1005
1034
      easy->result = Curl_http_connect(easy->easy_conn, &protocol_connect);
1006
1035
 
1007
 
      if(CURLE_OK == easy->result) {
 
1036
      if(easy->easy_conn->bits.proxy_connect_closed) {
 
1037
        /* reset the error buffer */
 
1038
        if(easy->easy_handle->set.errorbuffer)
 
1039
          easy->easy_handle->set.errorbuffer[0] = '\0';
 
1040
        easy->easy_handle->state.errorbuf = FALSE;
 
1041
 
 
1042
        easy->result = CURLE_OK;
 
1043
        result = CURLM_CALL_MULTI_PERFORM;
 
1044
        multistate(easy, CURLM_STATE_CONNECT);
 
1045
      }
 
1046
      else if (CURLE_OK == easy->result) {
1008
1047
        if(!easy->easy_conn->bits.tunnel_connecting)
1009
1048
          multistate(easy, CURLM_STATE_WAITCONNECT);
1010
1049
      }
1198
1237
      break;
1199
1238
 
1200
1239
    case CURLM_STATE_WAITPERFORM:
1201
 
#ifdef CURLDEBUG
1202
 
      infof(easy->easy_handle, "Conn %d recv pipe %d inuse %d athead %d\n",
1203
 
            easy->easy_conn->connectindex,
1204
 
            easy->easy_conn->recv_pipe->size,
1205
 
            easy->easy_conn->readchannel_inuse,
1206
 
            isHandleAtHead(easy->easy_handle,
1207
 
                           easy->easy_conn->recv_pipe));
1208
 
#endif
1209
1240
      /* Wait for our turn to PERFORM */
1210
1241
      if(!easy->easy_conn->readchannel_inuse &&
1211
1242
         isHandleAtHead(easy->easy_handle,
1215
1246
        multistate(easy, CURLM_STATE_PERFORM);
1216
1247
        result = CURLM_CALL_MULTI_PERFORM;
1217
1248
      }
 
1249
#ifdef CURLDEBUG
 
1250
      else {
 
1251
        infof(easy->easy_handle, "Conn %d recv pipe %d inuse %d athead %d\n",
 
1252
              easy->easy_conn->connectindex,
 
1253
              easy->easy_conn->recv_pipe->size,
 
1254
              easy->easy_conn->readchannel_inuse,
 
1255
              isHandleAtHead(easy->easy_handle,
 
1256
                             easy->easy_conn->recv_pipe));
 
1257
      }
 
1258
#endif
1218
1259
      break;
1219
1260
 
1220
1261
    case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */
1263
1304
 
1264
1305
      if(easy->result)  {
1265
1306
        /* The transfer phase returned error, we mark the connection to get
1266
 
         * closed to prevent being re-used. This is because we can't
1267
 
         * possibly know if the connection is in a good shape or not now. */
1268
 
        easy->easy_conn->bits.close = TRUE;
1269
 
        Curl_removeHandleFromPipeline(easy->easy_handle,
1270
 
                                      easy->easy_conn->recv_pipe);
 
1307
         * closed to prevent being re-used. This is because we can't possibly
 
1308
         * know if the connection is in a good shape or not now.  Unless it is
 
1309
         * a protocol which uses two "channels" like FTP, as then the error
 
1310
         * happened in the data connection.
 
1311
         */
 
1312
        if(!(easy->easy_conn->protocol & PROT_DUALCHANNEL))
 
1313
          easy->easy_conn->bits.close = TRUE;
1271
1314
 
1272
 
        if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) {
1273
 
          /* if we failed anywhere, we must clean up the secondary socket if
1274
 
             it was used */
1275
 
          sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
1276
 
          easy->easy_conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
1277
 
        }
1278
1315
        Curl_posttransfer(easy->easy_handle);
1279
1316
        Curl_done(&easy->easy_conn, easy->result, FALSE);
1280
1317
      }
1297
1334
        /* Check if we can move pending requests to send pipe */
1298
1335
        checkPendPipeline(easy->easy_conn);
1299
1336
 
1300
 
        /* When we follow redirects, must to go back to the CONNECT state */
 
1337
        /* When we follow redirects or is set to retry the connection, we must
 
1338
           to go back to the CONNECT state */
1301
1339
        if(easy->easy_handle->req.newurl || retry) {
1302
1340
          if(!retry) {
1303
1341
            /* if the URL is a follow-location and not just a retried request
1322
1360
        }
1323
1361
        else {
1324
1362
          /* after the transfer is done, go DONE */
 
1363
 
 
1364
          /* but first check to see if we got a location info even though we're
 
1365
             not following redirects */
 
1366
          if (easy->easy_handle->req.location) {
 
1367
            newurl = easy->easy_handle->req.location;
 
1368
            easy->easy_handle->req.location = NULL;
 
1369
            easy->result = Curl_follow(easy->easy_handle, newurl, FOLLOW_FAKE);
 
1370
            if (easy->result)
 
1371
              free(newurl);
 
1372
          }
 
1373
 
1325
1374
          multistate(easy, CURLM_STATE_DONE);
1326
1375
          result = CURLM_CALL_MULTI_PERFORM;
1327
1376
        }
1412
1461
    }
1413
1462
 
1414
1463
    /* now add a node to the Curl_message linked list with this info */
1415
 
    msg = (struct Curl_message *)malloc(sizeof(struct Curl_message));
 
1464
    msg = malloc(sizeof(struct Curl_message));
1416
1465
 
1417
1466
    if(!msg)
1418
1467
      return CURLM_OUT_OF_MEMORY;
1489
1538
 
1490
1539
  if( CURLM_OK >= returncode )
1491
1540
    update_timer(multi);
 
1541
 
1492
1542
  return returncode;
1493
1543
}
1494
1544
 
1513
1563
    multi->type = 0; /* not good anymore */
1514
1564
    Curl_hash_destroy(multi->hostcache);
1515
1565
    Curl_hash_destroy(multi->sockhash);
 
1566
    multi->hostcache = NULL;
 
1567
    multi->sockhash = NULL;
1516
1568
 
1517
1569
    /* go over all connections that have close actions */
1518
1570
    for(i=0; i< multi->connc->num; i++) {
1613
1665
  curl_socket_t s;
1614
1666
  int num;
1615
1667
  unsigned int curraction;
 
1668
  struct Curl_one_easy *easy_by_hash;
 
1669
  bool remove_sock_from_hash;
1616
1670
 
1617
1671
  memset(&socks, 0, sizeof(socks));
1618
1672
  for(i=0; i< MAX_SOCKSPEREASYHANDLE; i++)
1680
1734
      }
1681
1735
    }
1682
1736
    if(s != CURL_SOCKET_BAD) {
1683
 
      /* this socket has been removed. Remove it */
 
1737
 
 
1738
      /* this socket has been removed. Tell the app to remove it */
 
1739
      remove_sock_from_hash = TRUE;
1684
1740
 
1685
1741
      entry = Curl_hash_pick(multi->sockhash, (char *)&s, sizeof(s));
1686
1742
      if(entry) {
 
1743
        /* check if the socket to be removed serves a connection which has
 
1744
           other easy-s in a pipeline. In this case the socket should not be
 
1745
           removed. */
 
1746
        struct connectdata *easy_conn;
 
1747
 
 
1748
        easy_by_hash = entry->easy->multi_pos;
 
1749
        easy_conn = easy_by_hash->easy_conn;
 
1750
        if(easy_conn) {
 
1751
          if (easy_conn->recv_pipe && easy_conn->recv_pipe->size > 1) {
 
1752
            /* the handle should not be removed from the pipe yet */
 
1753
            remove_sock_from_hash = FALSE;
 
1754
 
 
1755
            /* Update the sockhash entry to instead point to the next in line
 
1756
               for the recv_pipe, or the first (in case this particular easy
 
1757
               isn't already) */
 
1758
            if (entry->easy == easy->easy_handle) {
 
1759
              if (isHandleAtHead(easy->easy_handle, easy_conn->recv_pipe))
 
1760
                entry->easy = easy_conn->recv_pipe->head->next->ptr;
 
1761
              else
 
1762
                entry->easy = easy_conn->recv_pipe->head->ptr;
 
1763
            }
 
1764
          }
 
1765
          if (easy_conn->send_pipe  && easy_conn->send_pipe->size > 1) {
 
1766
            /* the handle should not be removed from the pipe yet */
 
1767
            remove_sock_from_hash = FALSE;
 
1768
 
 
1769
            /* Update the sockhash entry to instead point to the next in line
 
1770
               for the send_pipe, or the first (in case this particular easy
 
1771
               isn't already) */
 
1772
            if (entry->easy == easy->easy_handle) {
 
1773
              if (isHandleAtHead(easy->easy_handle, easy_conn->send_pipe))
 
1774
                entry->easy = easy_conn->send_pipe->head->next->ptr;
 
1775
              else
 
1776
                entry->easy = easy_conn->send_pipe->head->ptr;
 
1777
            }
 
1778
          }
 
1779
          /* Don't worry about overwriting recv_pipe head with send_pipe_head,
 
1780
             when action will be asked on the socket (see multi_socket()), the
 
1781
             head of the correct pipe will be taken according to the
 
1782
             action. */
 
1783
        }
 
1784
      }
 
1785
      else
1687
1786
        /* just a precaution, this socket really SHOULD be in the hash already
1688
1787
           but in case it isn't, we don't have to tell the app to remove it
1689
1788
           either since it never got to know about it */
 
1789
        remove_sock_from_hash = FALSE;
 
1790
 
 
1791
      if (remove_sock_from_hash) {
1690
1792
        multi->socket_cb(easy->easy_handle,
1691
1793
                         s,
1692
1794
                         CURL_POLL_REMOVE,
1693
1795
                         multi->socket_userp,
1694
1796
                         entry ? entry->socketp : NULL);
1695
 
 
1696
1797
        sh_delentry(multi->sockhash, s);
1697
1798
      }
 
1799
 
1698
1800
    }
1699
1801
  }
1700
1802
 
1747
1849
        /* bad bad bad bad bad bad bad */
1748
1850
        return CURLM_INTERNAL_ERROR;
1749
1851
 
 
1852
      /* If the pipeline is enabled, take the handle which is in the head of
 
1853
         the pipeline. If we should write into the socket, take the send_pipe
 
1854
         head.  If we should read from the socket, take the recv_pipe head. */
 
1855
      if(data->set.one_easy->easy_conn) {
 
1856
        if ((ev_bitmask & CURL_POLL_OUT) &&
 
1857
            data->set.one_easy->easy_conn->send_pipe &&
 
1858
            data->set.one_easy->easy_conn->send_pipe->head)
 
1859
          data = data->set.one_easy->easy_conn->send_pipe->head->ptr;
 
1860
        else
 
1861
        if ((ev_bitmask & CURL_POLL_IN) &&
 
1862
            data->set.one_easy->easy_conn->recv_pipe &&
 
1863
            data->set.one_easy->easy_conn->recv_pipe->head)
 
1864
          data = data->set.one_easy->easy_conn->recv_pipe->head->ptr;
 
1865
      }
 
1866
 
1750
1867
      if(data->set.one_easy->easy_conn)  /* set socket event bitmask */
1751
1868
        data->set.one_easy->easy_conn->cselect_bits = ev_bitmask;
1752
1869
 
1897
2014
    /* splay the lowest to the bottom */
1898
2015
    multi->timetree = Curl_splay(tv_zero, multi->timetree);
1899
2016
 
1900
 
    if(Curl_splaycomparekeys(multi->timetree->key, now) > 0)
 
2017
    if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) {
1901
2018
      /* some time left before expiration */
1902
2019
      *timeout_ms = curlx_tvdiff(multi->timetree->key, now);
 
2020
      if(!*timeout_ms)
 
2021
        /*
 
2022
         * Since we only provide millisecond resolution on the returned value
 
2023
         * and the diff might be less than one millisecond here, we don't
 
2024
         * return zero as that may cause short bursts of busyloops on fast
 
2025
         * processors while the diff is still present but less than one
 
2026
         * millisecond! instead we return 1 until the time is ripe.
 
2027
         */
 
2028
        *timeout_ms=1;
 
2029
    }
1903
2030
    else
1904
2031
      /* 0 means immediately */
1905
2032
      *timeout_ms = 0;
1973
2100
  int result = 0;
1974
2101
  struct curl_llist_element *sendhead = conn->send_pipe->head;
1975
2102
 
1976
 
  if (conn->server_supports_pipelining) {
1977
 
    size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
 
2103
  size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
 
2104
  if (conn->server_supports_pipelining || pipeLen == 0) {
1978
2105
    struct curl_llist_element *curr = conn->pend_pipe->head;
 
2106
    const size_t maxPipeLen =
 
2107
      conn->server_supports_pipelining ? MAX_PIPELINE_LENGTH : 1;
1979
2108
 
1980
 
    while(pipeLen < MAX_PIPELINE_LENGTH && curr) {
 
2109
    while(pipeLen < maxPipeLen && curr) {
1981
2110
      Curl_llist_move(conn->pend_pipe, curr,
1982
2111
                      conn->send_pipe, conn->send_pipe->tail);
1983
 
      Curl_pgrsTime(curr->ptr, TIMER_CONNECT);
 
2112
      Curl_pgrsTime(curr->ptr, TIMER_PRETRANSFER);
1984
2113
      ++result; /* count how many handles we moved */
1985
2114
      curr = conn->pend_pipe->head;
1986
2115
      ++pipeLen;
1987
2116
    }
1988
 
    if (result > 0)
1989
 
      conn->now = Curl_tvnow();
1990
2117
  }
1991
2118
 
1992
 
  if(result) {
 
2119
  if (result) {
 
2120
    conn->now = Curl_tvnow();
1993
2121
    /* something moved, check for a new send pipeline leader */
1994
2122
    if(sendhead != conn->send_pipe->head) {
1995
2123
      /* this is a new one as head, expire it */
2050
2178
}
2051
2179
 
2052
2180
/* given a number of milliseconds from now to use to set the 'act before
2053
 
   this'-time for the transfer, to be extracted by curl_multi_timeout() */
 
2181
   this'-time for the transfer, to be extracted by curl_multi_timeout()
 
2182
 
 
2183
   Pass zero to clear the timeout value for this handle.
 
2184
*/
2054
2185
void Curl_expire(struct SessionHandle *data, long milli)
2055
2186
{
2056
2187
  struct Curl_multi *multi = data->multi;
2166
2297
                        struct SessionHandle *data)
2167
2298
{
2168
2299
  int i;
2169
 
  struct closure *cl = (struct closure *)calloc(sizeof(struct closure), 1);
 
2300
  struct closure *cl = calloc(sizeof(struct closure), 1);
2170
2301
  struct closure *p=NULL;
2171
2302
  struct closure *n;
2172
2303
  if(cl) {
2216
2347
}
2217
2348
 
2218
2349
#ifdef CURLDEBUG
2219
 
void curl_multi_dump(CURLM *multi_handle)
 
2350
void Curl_multi_dump(const struct Curl_multi *multi_handle)
2220
2351
{
2221
2352
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
2222
2353
  struct Curl_one_easy *easy;