Changeset 7855


Ignore:
Timestamp:
Feb 9, 2009, 5:34:43 PM (13 years ago)
Author:
charles
Message:

(1.5x libT) backport #1671, #1798

Location:
branches/1.5x/libtransmission
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • branches/1.5x/libtransmission/Makefile.am

    r7664 r7855  
    8181    peer-io.h \
    8282    peer-mgr.h \
    83     peer-mgr-private.h \
    8483    peer-msgs.h \
    8584    platform.h \
  • branches/1.5x/libtransmission/fastresume.c

    r7722 r7855  
    2222 * DEALINGS IN THE SOFTWARE.
    2323 *****************************************************************************/
     24
     25/*
     26 * NOTE: THIS FILE IS DEPRECATED
     27 *
     28 *  The fastresume file format is brittle and was replaced in Transmission 1.20
     29 *  with the benc-formatted ".resume" files implemented in resume.[ch].
     30 *
     31 *  This older format is kept only for reading older resume files for users
     32 *  who upgrade from older versions of Transmission, and may be removed
     33 *  after more time has passed.
     34 */ 
    2435
    2536/***********************************************************************
  • branches/1.5x/libtransmission/rpcimpl.c

    r7813 r7855  
    983983        tr_bencFree( &response );
    984984    }
    985 
    986     if( methods[i].immediate )
     985    else if( methods[i].immediate )
    987986    {
    988987        int64_t tag;
     
    10421041}
    10431042
    1044 static void
    1045 addToken( tr_benc *    list,
    1046           const char * token,
    1047           size_t       len )
    1048 {
    1049     char *       p;
    1050     const char * end = token + len;
    1051     const long   a = strtol( token, &p, 10 );
    1052 
    1053     if( p == end )
    1054         tr_bencListAddInt( list, a );
    1055     else if( *p == '-' && isdigit( p[1] ) )
    1056     {
    1057         const long b = strtol( p + 1, &p, 10 );
    1058         if( ( p == end ) && ( b > a ) )
    1059         {
    1060             long i;
    1061             for( i = a; i <= b; ++i )
    1062                 tr_bencListAddInt( list, i );
    1063         }
    1064     }
    1065 }
    1066 
    10671043/**
    10681044 * Munge the URI into a usable form.
     
    10761052void
    10771053tr_rpc_parse_list_str( tr_benc     * setme,
    1078                        const char  * str_in,
     1054                       const char  * str,
    10791055                       int           len )
    10801056
    10811057{
    1082     char *       str = tr_strndup( str_in, len );
    1083     int          isNum;
    1084     int          isNumList;
    1085     int          commaCount;
    1086     const char * walk;
    1087 
    1088     isNum = 1;
    1089     isNumList = 1;
    1090     commaCount = 0;
    1091     walk = str;
    1092     for( ; *walk && ( isNumList || isNum ); ++walk )
    1093     {
    1094         if( isNumList ) isNumList = *walk == '-' || isdigit( *walk )
    1095                                     || *walk == ',';
    1096         if( isNum     ) isNum     = *walk == '-' || isdigit( *walk );
    1097         if( *walk == ',' ) ++commaCount;
    1098     }
    1099 
    1100     if( isNum )
    1101         tr_bencInitInt( setme, strtol( str, NULL, 10 ) );
    1102     else if( !isNumList )
     1058    int valueCount;
     1059    int * values = tr_parseNumberRange( str, len, &valueCount );
     1060
     1061    if( valueCount == 0 )
    11031062        tr_bencInitStr( setme, str, len );
    1104     else
    1105     {
    1106         tr_bencInitList( setme, commaCount + 1 );
    1107         walk = str;
    1108         while( *walk )
    1109         {
    1110             const char * p = strchr( walk, ',' );
    1111             if( !p )
    1112                 p = walk + strlen( walk );
    1113             addToken( setme, walk, p - walk );
    1114             if( *p != ',' )
    1115                 break;
    1116             walk = p + 1;
    1117         }
    1118     }
    1119 
    1120     tr_free( str );
     1063    else if( valueCount == 1 )
     1064        tr_bencInitInt( setme, values[0] );
     1065    else {
     1066        int i;
     1067        tr_bencInitList( setme, valueCount );
     1068        for( i=0; i<valueCount; ++i )
     1069            tr_bencListAddInt( setme, values[i] );
     1070    }
     1071
     1072    tr_free( values );
    11211073}
    11221074
  • branches/1.5x/libtransmission/torrent.c

    r7813 r7855  
    10011001    assert( pieceIndex < tor->info.pieceCount );
    10021002
    1003     tr_torrentLock( tor );
    1004 
    10051003    if( has )
    10061004        tr_cpPieceAdd( &tor->completion, pieceIndex );
    10071005    else
    10081006        tr_cpPieceRem( &tor->completion, pieceIndex );
    1009 
    1010     tr_torrentUnlock( tor );
    10111007}
    10121008
  • branches/1.5x/libtransmission/utils-test.c

    r7813 r7855  
    186186}
    187187
     188static int
     189test_numbers( void )
     190{
     191    int i;
     192    int count;
     193    int * numbers;
     194
     195    numbers = tr_parseNumberRange( "1-10,13,16-19", -1, &count );
     196    check( count == 15 );
     197    check( numbers != NULL );
     198    check( numbers[0] == 1 );
     199    check( numbers[5] == 6 );
     200    check( numbers[9] == 10 );
     201    check( numbers[10] == 13 );
     202    check( numbers[11] == 16 );
     203    check( numbers[14] == 19 );
     204    tr_free( numbers );
     205
     206    numbers = tr_parseNumberRange( "1-5,3-7,2-6", -1, &count );
     207    check( count == 7 );
     208    check( numbers != NULL );
     209    for( i=0; i<count; ++i )
     210        check( numbers[i] == i+1 );
     211    tr_free( numbers );
     212
     213    numbers = tr_parseNumberRange( "1-Hello", -1, &count );
     214    check( count == 0 );
     215    check( numbers == NULL );
     216
     217    numbers = tr_parseNumberRange( "1-", -1, &count );
     218    check( count == 0 );
     219    check( numbers == NULL );
     220
     221    numbers = tr_parseNumberRange( "Hello", -1, &count );
     222    check( count == 0 );
     223    check( numbers == NULL );
     224
     225    return 0;
     226}
     227
    188228int
    189229main( void )
     
    212252    if( ( i = test_utf8( ) ) )
    213253        return i;
     254    if( ( i = test_numbers( ) ) )
     255        return i;
    214256
    215257    /* test that tr_cryptoRandInt() stays in-bounds */
  • branches/1.5x/libtransmission/utils.c

    r7813 r7855  
    13791379    return ret;
    13801380}
     1381
     1382/***
     1383****
     1384***/
     1385
     1386struct number_range
     1387{
     1388    int low;
     1389    int high;
     1390};
     1391
     1392/**
     1393 * This should be a single number (ex. "6") or a range (ex. "6-9").
     1394 * Anything else is an error and will return failure.
     1395 */
     1396static tr_bool
     1397parseNumberSection( const char * str, int len, struct number_range * setme )
     1398{
     1399    long a, b;
     1400    tr_bool success;
     1401    char * end;
     1402    const int error = errno;
     1403    char * tmp = tr_strndup( str, len );
     1404
     1405    errno = 0;
     1406    a = strtol( tmp, &end, 10 );
     1407    if( errno || ( end == tmp ) ) {
     1408        success = FALSE;
     1409    } else if( *end != '-' ) {
     1410        b = a;
     1411        success = TRUE;
     1412    } else {
     1413        const char * pch = end + 1;
     1414        b = strtol( pch, &end, 10 );
     1415        if( errno || ( pch == end ) )
     1416            success = FALSE;
     1417        else if( *end ) /* trailing data */
     1418            success = FALSE;
     1419        else
     1420            success = TRUE;
     1421    }
     1422    tr_free( tmp );
     1423
     1424    setme->low = MIN( a, b );
     1425    setme->high = MAX( a, b );
     1426
     1427    errno = error;
     1428    return success;
     1429}
     1430
     1431static int
     1432compareInt( const void * va, const void * vb )
     1433{
     1434    const int a = *(const int *)va;
     1435    const int b = *(const int *)vb;
     1436    return a - b;
     1437}
     1438
     1439/**
     1440 * Given a string like "1-4" or "1-4,6,9,14-51", this allocates and returns an
     1441 * array of setmeCount ints of all the values in the array.
     1442 * For example, "5-8" will return [ 5, 6, 7, 8 ] and setmeCount will be 4.
     1443 * It's the caller's responsibility to call tr_free() on the returned array.
     1444 * If a fragment of the string can't be parsed, NULL is returned.
     1445 */
     1446int*
     1447tr_parseNumberRange( const char * str_in, int len, int * setmeCount )
     1448{
     1449    int n = 0;
     1450    int * uniq = NULL;
     1451    char * str = tr_strndup( str_in, len );
     1452    const char * walk;
     1453    tr_list * ranges = NULL;
     1454    tr_bool success = TRUE;
     1455
     1456    walk = str;
     1457    while( walk && *walk && success ) {
     1458        struct number_range range;
     1459        const char * pch = strchr( walk, ',' );
     1460        if( pch ) {
     1461            success = parseNumberSection( walk, pch-walk, &range );
     1462            walk = pch + 1;
     1463        } else {
     1464            success = parseNumberSection( walk, strlen( walk ), &range );
     1465            walk += strlen( walk );
     1466        }
     1467        if( success )
     1468            tr_list_append( &ranges, tr_memdup( &range, sizeof( struct number_range ) ) );
     1469    }
     1470
     1471    if( !success )
     1472    {
     1473        *setmeCount = 0;
     1474        uniq = NULL;
     1475    }
     1476    else
     1477    {
     1478        int i;
     1479        int n2;
     1480        tr_list * l;
     1481        int * sorted = NULL;
     1482
     1483        /* build a sorted number array */
     1484        n = n2 = 0;
     1485        for( l=ranges; l!=NULL; l=l->next ) {
     1486            const struct number_range * r = l->data;
     1487            n += r->high + 1 - r->low;
     1488        }
     1489        sorted = tr_new( int, n );
     1490        for( l=ranges; l!=NULL; l=l->next ) {
     1491            const struct number_range * r = l->data;
     1492            int i;
     1493            for( i=r->low; i<=r->high; ++i )
     1494                sorted[n2++] = i;
     1495        }
     1496        qsort( sorted, n, sizeof( int ), compareInt );
     1497        assert( n == n2 );
     1498
     1499        /* remove duplicates */
     1500        uniq = tr_new( int, n );
     1501        for( i=n=0; i<n2; ++i )
     1502            if( !n || uniq[n-1] != sorted[i] )
     1503                uniq[n++] = sorted[i];
     1504
     1505        tr_free( sorted );
     1506    }
     1507
     1508    /* cleanup */
     1509    tr_list_free( &ranges, tr_free );
     1510    tr_free( str );
     1511
     1512    /* return the result */
     1513    *setmeCount = n;
     1514    return uniq;
     1515}
  • branches/1.5x/libtransmission/utils.h

    r7813 r7855  
    8080
    8181#if !defined( _ )
    82  #if defined( SYS_DARWIN )
    83   #define _( a ) ( a )
    84  #elif defined( HAVE_LIBINTL_H )
     82 #if defined( HAVE_LIBINTL_H ) && !defined( SYS_DARWIN )
    8583  #include <libintl.h>
    8684  #define _( a ) gettext ( a )
     
    454452double tr_getRatio( double numerator, double denominator );
    455453
     454int* tr_parseNumberRange( const char * str, int str_len, int * setmeCount );
     455
    456456
    457457int tr_ptr2int( void* );
  • branches/1.5x/libtransmission/web.c

    r7813 r7855  
    5050#endif
    5151
     52struct tr_web_sockinfo
     53{
     54    int fd;
     55    tr_bool evset;
     56    struct event ev;
     57};
     58
    5259struct tr_web
    5360{
     
    5865    CURLM * multi;
    5966    tr_session * session;
    60 #if 0
    61     tr_list * easy_queue;
    62 #endif
    6367    struct event timer_event;
     68    tr_list * fds;
    6469};
     70
     71/***
     72****
     73***/
     74
     75static struct tr_web_sockinfo *
     76getSockinfo( tr_web * web, int fd, tr_bool createIfMissing )
     77{
     78    tr_list * l = web->fds;
     79
     80    for( l=web->fds; l!=NULL; l=l->next ) {
     81        struct tr_web_sockinfo * s =  l->data;
     82        if( s->fd == fd ) {
     83            dbgmsg( "looked up sockinfo %p for fd %d", s, fd );
     84            return s;
     85        }
     86    }
     87
     88    if( createIfMissing ) {
     89        struct tr_web_sockinfo * s =  tr_new0( struct tr_web_sockinfo, 1 );
     90        s->fd = fd;
     91        tr_list_prepend( &web->fds, s );
     92        dbgmsg( "created sockinfo %p for fd %d... we now have %d sockinfos", s, fd, tr_list_size(web->fds) );
     93        return s;
     94    }
     95
     96    return NULL;
     97}
     98
     99static void
     100clearSockinfoEvent( struct tr_web_sockinfo * s )
     101{
     102    if( s && s->evset )
     103    {
     104        dbgmsg( "clearing libevent polling for sockinfo %p, fd %d", s, s->fd );
     105        event_del( &s->ev );
     106        s->evset = FALSE;
     107    }
     108}
     109
     110static void
     111purgeSockinfo( tr_web * web, int fd )
     112{
     113    struct tr_web_sockinfo * s = getSockinfo( web, fd, FALSE );
     114
     115    if( s != NULL )
     116    {
     117        tr_list_remove_data( &web->fds, s );
     118        clearSockinfoEvent( s );
     119        dbgmsg( "freeing sockinfo %p, fd %d", s, s->fd );
     120        tr_free( s );
     121    }
     122}
    65123
    66124/***
     
    128186        }
    129187
     188        curl_easy_setopt( easy, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
    130189        curl_easy_setopt( easy, CURLOPT_DNS_CACHE_TIMEOUT, 360L );
    131190        curl_easy_setopt( easy, CURLOPT_CONNECTTIMEOUT, 60L );
     
    149208            curl_easy_setopt( easy, CURLOPT_ENCODING, "" );
    150209
    151 #if 0
    152         if( web->still_running >= MAX_CONCURRENT_TASKS )
    153210        {
    154             tr_list_append( &web->easy_queue, easy );
    155             dbgmsg( " >> enqueueing a task ... size is now %d",
    156                                            tr_list_size( web->easy_queue ) );
    157         }
    158         else
    159 #endif
    160         {
    161             const CURLMcode rc = curl_multi_add_handle( web->multi, easy );
    162             if( rc == CURLM_OK )
     211            const CURLMcode mcode = curl_multi_add_handle( web->multi, easy );
     212            tr_assert( mcode == CURLM_OK, "curl_multi_add_handle() failed: %d (%s)", mcode, curl_multi_strerror( mcode ) );
     213            if( mcode == CURLM_OK )
    163214                ++web->still_running;
    164215            else
    165                 tr_err( "%s", curl_multi_strerror( rc ) );
     216                tr_err( "%s", curl_multi_strerror( mcode ) );
    166217        }
    167218    }
     
    171222****
    172223***/
    173 
    174 struct tr_web_sockinfo
    175 {
    176     struct event ev;
    177     int evset;
    178 };
    179224
    180225static void
     
    220265        if( easy ) {
    221266            long code;
     267            long fd;
    222268            struct tr_web_task * task;
    223             curl_easy_getinfo( easy, CURLINFO_PRIVATE, (void*)&task );
    224             curl_easy_getinfo( easy, CURLINFO_RESPONSE_CODE, &code );
    225             curl_multi_remove_handle( g->multi, easy );
     269            CURLcode ecode;
     270            CURLMcode mcode;
     271
     272            ecode = curl_easy_getinfo( easy, CURLINFO_PRIVATE, (void*)&task );
     273            tr_assert( ecode == CURLE_OK, "curl_easy_getinfo() failed: %d (%s)", ecode, curl_easy_strerror( ecode ) );
     274
     275            ecode = curl_easy_getinfo( easy, CURLINFO_RESPONSE_CODE, &code );
     276            tr_assert( ecode == CURLE_OK, "curl_easy_getinfo() failed: %d (%s)", ecode, curl_easy_strerror( ecode ) );
     277
     278            ecode = curl_easy_getinfo( easy, CURLINFO_LASTSOCKET, &fd );
     279            tr_assert( ecode == CURLE_OK, "curl_easy_getinfo() failed: %d (%s)", ecode, curl_easy_strerror( ecode ) );
     280            if( fd != -1L )
     281                purgeSockinfo( g, fd );
     282
     283            mcode = curl_multi_remove_handle( g->multi, easy );
     284            tr_assert( mcode == CURLM_OK, "curl_multi_socket_action() failed: %d (%s)", mcode, curl_multi_strerror( mcode ) );
     285
    226286            curl_easy_cleanup( easy );
    227287            task_finish( task, code );
     
    258318}
    259319
    260 #if 0
    261 static void
    262 add_tasks_from_queue( tr_web * g )
    263 {
    264     while( ( g->still_running < MAX_CONCURRENT_TASKS )
    265         && ( tr_list_size( g->easy_queue ) > 0 ) )
    266     {
    267         CURL * easy = tr_list_pop_front( &g->easy_queue );
    268         if( easy )
    269         {
    270             const CURLMcode rc = curl_multi_add_handle( g->multi, easy );
    271             if( rc != CURLM_OK )
    272                 tr_err( "%s", curl_multi_strerror( rc ) );
    273             else {
    274                 dbgmsg( "pumped the task queue, %d remain",
    275                         tr_list_size( g->easy_queue ) );
    276                 ++g->still_running;
    277             }
    278         }
    279     }
    280 }
    281 #endif
    282 
    283320static void
    284321web_close( tr_web * g )
    285322{
     323    CURLMcode mcode;
     324
    286325    stop_timer( g );
    287     curl_multi_cleanup( g->multi );
     326
     327    mcode = curl_multi_cleanup( g->multi );
     328    tr_assert( mcode == CURLM_OK, "curl_multi_cleanup() failed: %d (%s)", mcode, curl_multi_strerror( mcode ) );
     329    if( mcode != CURLM_OK )
     330        tr_err( "%s", curl_multi_strerror( mcode ) );
     331
    288332    tr_free( g );
    289333}
     
    296340{
    297341    int closed = FALSE;
    298     CURLMcode rc;
     342    CURLMcode mcode;
    299343
    300344    dbgmsg( "check_run_count: prev_running %d, still_running %d",
     
    303347    /* invoke libcurl's processing */
    304348    do {
    305         rc = curl_multi_socket_action( g->multi, fd, 0, &g->still_running );
    306         dbgmsg( "event_cb(): fd %d, still_running is %d",
    307                 fd, g->still_running );
    308     } while( rc == CURLM_CALL_MULTI_PERFORM );
    309     if( rc != CURLM_OK )
    310         tr_err( "%s", curl_multi_strerror( rc ) );
     349        mcode = curl_multi_socket_action( g->multi, fd, 0, &g->still_running );
     350        dbgmsg( "event_cb(): fd %d, still_running is %d", fd, g->still_running );
     351    } while( mcode == CURLM_CALL_MULTI_PERFORM );
     352    if( ( mcode == CURLM_BAD_SOCKET ) && ( fd != CURL_SOCKET_TIMEOUT ) )
     353        purgeSockinfo( g, fd );
     354    else {
     355        tr_assert( mcode == CURLM_OK, "curl_multi_socket_action() failed on fd %d: %d (%s)", fd, mcode, curl_multi_strerror( mcode ) );
     356        if( mcode != CURLM_OK )
     357            tr_err( "%s", curl_multi_strerror( mcode ) );
     358    }
    311359
    312360    remove_finished_tasks( g );
    313361
    314 #if 0
    315     add_tasks_from_queue( g );
    316 #endif
    317 
    318362    if( !g->still_running ) {
     363        assert( tr_list_size( g->fds ) == 0 );
    319364        stop_timer( g );
    320365        if( g->closing ) {
     
    343388}
    344389
    345 static void
    346 remsock( struct tr_web_sockinfo * f )
    347 {
    348     if( f ) {
    349         dbgmsg( "deleting sockinfo %p", f );
    350         if( f->evset )
    351             event_del( &f->ev );
    352         tr_free( f );
    353     }
    354 }
    355 
    356 static void
    357 setsock( curl_socket_t            sockfd,
    358          int                      action,
    359          struct tr_web          * g,
    360          struct tr_web_sockinfo * f )
    361 {
    362     const int kind = EV_PERSIST
    363                    | (( action & CURL_POLL_IN ) ? EV_READ : 0 )
    364                    | (( action & CURL_POLL_OUT ) ? EV_WRITE : 0 );
    365 
    366     assert( tr_amInEventThread( g->session ) );
    367     assert( g->session != NULL );
    368     assert( g->session->events != NULL );
    369 
    370     dbgmsg( "setsock: fd is %d, curl action is %d, libevent action is %d",
    371             sockfd, action, kind );
    372     if( f->evset )
    373         event_del( &f->ev );
    374     event_set( &f->ev, sockfd, kind, event_cb, g );
    375     f->evset = 1;
    376     event_add( &f->ev, NULL );
    377 }
    378 
    379 static void
    380 addsock( curl_socket_t    sockfd,
    381          int              action,
    382          struct tr_web  * g )
    383 {
    384     struct tr_web_sockinfo * f = tr_new0( struct tr_web_sockinfo, 1 );
    385     dbgmsg( "creating a sockinfo %p for fd %d", f, sockfd );
    386     setsock( sockfd, action, g, f );
    387     curl_multi_assign( g->multi, sockfd, f );
    388 }
    389 
    390390/* CURLMOPT_SOCKETFUNCTION */
    391391static int
    392392sock_cb( CURL            * e UNUSED,
    393          curl_socket_t     s,
    394          int               what,
    395          void            * vg,
    396          void            * vf)
    397 {
    398     struct tr_web * g = vg;
    399     struct tr_web_sockinfo * f = vf;
    400     dbgmsg( "sock_cb: what is %d, sockinfo is %p", what, f );
    401 
    402     if( what == CURL_POLL_REMOVE )
    403         remsock( f );
    404     else if( !f )
    405         addsock( s, what, g );
     393         curl_socket_t     fd,
     394         int               action,
     395         void            * vweb,
     396         void            * unused UNUSED)
     397{
     398    struct tr_web * web = vweb;
     399    dbgmsg( "sock_cb: action is %d, fd is %d", action, (int)fd );
     400
     401    if( action == CURL_POLL_REMOVE )
     402    {
     403        purgeSockinfo( web, fd );
     404    }
    406405    else
    407         setsock( s, what, g, f );
     406    {
     407        struct tr_web_sockinfo * sockinfo = getSockinfo( web, fd, TRUE );
     408        const int kind = EV_PERSIST
     409                       | (( action & CURL_POLL_IN ) ? EV_READ : 0 )
     410                       | (( action & CURL_POLL_OUT ) ? EV_WRITE : 0 );
     411        dbgmsg( "setsock: fd is %d, curl action is %d, libevent action is %d", fd, action, kind );
     412        assert( tr_amInEventThread( web->session ) );
     413        assert( kind != EV_PERSIST );
     414
     415        /* clear any old polling on this fd */
     416        clearSockinfoEvent( sockinfo );
     417
     418        /* set the new polling on this fd */
     419        dbgmsg( "enabling (libevent %d, libcurl %d) polling on sockinfo %p, fd %d", action, kind, sockinfo, fd );
     420        event_set( &sockinfo->ev, fd, kind, event_cb, web );
     421        event_add( &sockinfo->ev, NULL );
     422        sockinfo->evset = TRUE;
     423    }
    408424
    409425    return 0;
     
    462478tr_webInit( tr_session * session )
    463479{
     480    CURLMcode mcode;
    464481    static int curlInited = FALSE;
    465482    tr_web * web;
     
    480497
    481498    evtimer_set( &web->timer_event, timer_cb, web );
    482     curl_multi_setopt( web->multi, CURLMOPT_SOCKETDATA, web );
    483     curl_multi_setopt( web->multi, CURLMOPT_SOCKETFUNCTION, sock_cb );
    484     curl_multi_setopt( web->multi, CURLMOPT_TIMERDATA, web );
    485     curl_multi_setopt( web->multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb );
     499    mcode = curl_multi_setopt( web->multi, CURLMOPT_SOCKETDATA, web );
     500    tr_assert( mcode == CURLM_OK, "curl_mutli_setopt() failed: %d (%s)", mcode, curl_multi_strerror( mcode ) );
     501    mcode = curl_multi_setopt( web->multi, CURLMOPT_SOCKETFUNCTION, sock_cb );
     502    tr_assert( mcode == CURLM_OK, "curl_mutli_setopt() failed: %d (%s)", mcode, curl_multi_strerror( mcode ) );
     503    mcode = curl_multi_setopt( web->multi, CURLMOPT_TIMERDATA, web );
     504    tr_assert( mcode == CURLM_OK, "curl_mutli_setopt() failed: %d (%s)", mcode, curl_multi_strerror( mcode ) );
     505    mcode = curl_multi_setopt( web->multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb );
     506    tr_assert( mcode == CURLM_OK, "curl_mutli_setopt() failed: %d (%s)", mcode, curl_multi_strerror( mcode ) );
    486507
    487508    return web;
Note: See TracChangeset for help on using the changeset viewer.