(trunk libT) new peer request fifo queue with log(N) search time. new unit tests for the queue. new utility tr_lowerBound()
This commit is contained in:
parent
a0390c6111
commit
51e68d6e56
|
@ -39,6 +39,7 @@ libtransmission_a_SOURCES = \
|
|||
ptrarray.c \
|
||||
publish.c \
|
||||
ratecontrol.c \
|
||||
request-list.c \
|
||||
resume.c \
|
||||
rpcimpl.c \
|
||||
rpc-server.c \
|
||||
|
@ -86,6 +87,7 @@ noinst_HEADERS = \
|
|||
ptrarray.h \
|
||||
publish.h \
|
||||
ratecontrol.h \
|
||||
request-list.h \
|
||||
resume.h \
|
||||
rpcimpl.h \
|
||||
rpc-server.h \
|
||||
|
@ -108,6 +110,7 @@ TESTS = \
|
|||
clients-test \
|
||||
json-test \
|
||||
peer-msgs-test \
|
||||
request-list-test \
|
||||
rpc-test \
|
||||
test-peer-id \
|
||||
utils-test
|
||||
|
@ -153,6 +156,10 @@ test_peer_id_SOURCES = test-peer-id.c
|
|||
test_peer_id_LDADD = ${apps_ldadd}
|
||||
test_peer_id_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
request_list_test_SOURCES = request-list-test.c
|
||||
request_list_test_LDADD = ${apps_ldadd}
|
||||
request_list_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
peer_msgs_test_SOURCES = peer-msgs-test.c
|
||||
peer_msgs_test_LDADD = ${apps_ldadd}
|
||||
peer_msgs_test_LDFLAGS = ${apps_ldflags}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "peer-msgs.h"
|
||||
#include "platform.h" /* MAX_STACK_ARRAY_SIZE */
|
||||
#include "ratecontrol.h"
|
||||
#include "request-list.h"
|
||||
#include "stats.h"
|
||||
#include "torrent.h"
|
||||
#include "trevent.h"
|
||||
|
@ -102,10 +103,6 @@ enum
|
|||
MAX_FAST_SET_SIZE = 3
|
||||
};
|
||||
|
||||
/**
|
||||
*** REQUEST MANAGEMENT
|
||||
**/
|
||||
|
||||
enum
|
||||
{
|
||||
AWAITING_BT_LENGTH,
|
||||
|
@ -114,124 +111,6 @@ enum
|
|||
AWAITING_BT_PIECE
|
||||
};
|
||||
|
||||
struct peer_request
|
||||
{
|
||||
uint32_t index;
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
time_t time_requested;
|
||||
};
|
||||
|
||||
static inline tr_bool
|
||||
requestsMatch( const struct peer_request * a, const struct peer_request * b )
|
||||
{
|
||||
return (a->index==b->index) && (a->offset==b->offset) && (a->length==b->length);
|
||||
}
|
||||
|
||||
struct request_list
|
||||
{
|
||||
uint16_t count;
|
||||
uint16_t max;
|
||||
struct peer_request * requests;
|
||||
};
|
||||
|
||||
static const struct request_list REQUEST_LIST_INIT = { 0, 0, NULL };
|
||||
|
||||
static void
|
||||
reqListReserve( struct request_list * list,
|
||||
uint16_t max )
|
||||
{
|
||||
if( list->max < max )
|
||||
{
|
||||
list->max = max;
|
||||
list->requests = tr_renew( struct peer_request,
|
||||
list->requests,
|
||||
list->max );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reqListClear( struct request_list * list )
|
||||
{
|
||||
tr_free( list->requests );
|
||||
*list = REQUEST_LIST_INIT;
|
||||
}
|
||||
|
||||
static void
|
||||
reqListCopy( struct request_list * dest, const struct request_list * src )
|
||||
{
|
||||
dest->count = dest->max = src->count;
|
||||
dest->requests = tr_memdup( src->requests, dest->count * sizeof( struct peer_request ) );
|
||||
}
|
||||
|
||||
static void
|
||||
reqListRemoveOne( struct request_list * list,
|
||||
int i )
|
||||
{
|
||||
assert( 0 <= i && i < list->count );
|
||||
|
||||
memmove( &list->requests[i],
|
||||
&list->requests[i + 1],
|
||||
sizeof( struct peer_request ) * ( --list->count - i ) );
|
||||
}
|
||||
|
||||
static void
|
||||
reqListAppend( struct request_list * list,
|
||||
const struct peer_request * req )
|
||||
{
|
||||
if( ++list->count >= list->max )
|
||||
reqListReserve( list, list->max + 8 );
|
||||
|
||||
list->requests[list->count - 1] = *req;
|
||||
}
|
||||
|
||||
static int
|
||||
reqListPop( struct request_list * list,
|
||||
struct peer_request * setme )
|
||||
{
|
||||
int success;
|
||||
|
||||
if( !list->count )
|
||||
success = FALSE;
|
||||
else {
|
||||
*setme = list->requests[0];
|
||||
reqListRemoveOne( list, 0 );
|
||||
success = TRUE;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static int
|
||||
reqListFind( struct request_list * list,
|
||||
const struct peer_request * key )
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
for( i = 0; i < list->count; ++i )
|
||||
if( requestsMatch( key, list->requests + i ) )
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
reqListRemove( struct request_list * list,
|
||||
const struct peer_request * key )
|
||||
{
|
||||
int success;
|
||||
const int i = reqListFind( list, key );
|
||||
|
||||
if( i < 0 )
|
||||
success = FALSE;
|
||||
else {
|
||||
reqListRemoveOne( list, i );
|
||||
success = TRUE;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
***
|
||||
**/
|
||||
|
@ -860,26 +739,22 @@ expireFromList( tr_peermsgs * msgs,
|
|||
struct request_list * list,
|
||||
const time_t oldestAllowed )
|
||||
{
|
||||
int i;
|
||||
size_t i;
|
||||
struct request_list tmp = REQUEST_LIST_INIT;
|
||||
|
||||
/* walk through the list, looking for the first req that's too old */
|
||||
for( i=0; i<list->count; ++i ) {
|
||||
const struct peer_request * req = &list->requests[i];
|
||||
if( req->time_requested < oldestAllowed )
|
||||
break;
|
||||
}
|
||||
/* since the fifo list is sorted by time, the oldest will be first */
|
||||
if( list->fifo[0].time_requested >= oldestAllowed )
|
||||
return;
|
||||
|
||||
/* if we found one too old, start pruning them */
|
||||
if( i < list->count ) {
|
||||
struct request_list tmp = REQUEST_LIST_INIT;
|
||||
reqListCopy( &tmp, list );
|
||||
for( ; i<tmp.count; ++i ) {
|
||||
const struct peer_request * req = &tmp.requests[i];
|
||||
if( req->time_requested < oldestAllowed )
|
||||
tr_peerMsgsCancel( msgs, req->index, req->offset, req->length );
|
||||
}
|
||||
reqListClear( &tmp );
|
||||
reqListCopy( &tmp, list );
|
||||
for( i=0; i<tmp.len; ++i ) {
|
||||
const struct peer_request * req = &tmp.fifo[i];
|
||||
if( req->time_requested >= oldestAllowed )
|
||||
break;
|
||||
tr_peerMsgsCancel( msgs, req->index, req->offset, req->length );
|
||||
}
|
||||
reqListClear( &tmp );
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -908,7 +783,7 @@ pumpRequestQueue( tr_peermsgs * msgs, const time_t now )
|
|||
{
|
||||
const int max = msgs->maxActiveRequests;
|
||||
int sent = 0;
|
||||
int count = msgs->clientAskedFor.count;
|
||||
int len = msgs->clientAskedFor.len;
|
||||
struct peer_request req;
|
||||
|
||||
if( msgs->peer->clientIsChoked )
|
||||
|
@ -916,7 +791,7 @@ pumpRequestQueue( tr_peermsgs * msgs, const time_t now )
|
|||
if( !tr_torrentIsPieceTransferAllowed( msgs->torrent, TR_PEER_TO_CLIENT ) )
|
||||
return;
|
||||
|
||||
while( ( count < max ) && reqListPop( &msgs->clientWillAskFor, &req ) )
|
||||
while( ( len < max ) && reqListPop( &msgs->clientWillAskFor, &req ) )
|
||||
{
|
||||
const tr_block_index_t block = _tr_block( msgs->torrent, req.index, req.offset );
|
||||
|
||||
|
@ -931,16 +806,16 @@ pumpRequestQueue( tr_peermsgs * msgs, const time_t now )
|
|||
req.time_requested = now;
|
||||
reqListAppend( &msgs->clientAskedFor, &req );
|
||||
|
||||
++count;
|
||||
++len;
|
||||
++sent;
|
||||
}
|
||||
}
|
||||
|
||||
if( sent )
|
||||
dbgmsg( msgs, "pump sent %d requests, now have %d active and %d queued",
|
||||
sent, msgs->clientAskedFor.count, msgs->clientWillAskFor.count );
|
||||
sent, msgs->clientAskedFor.len, msgs->clientWillAskFor.len );
|
||||
|
||||
if( count < max )
|
||||
if( len < max )
|
||||
fireNeedReq( msgs );
|
||||
}
|
||||
|
||||
|
@ -948,7 +823,7 @@ static inline int
|
|||
requestQueueIsFull( const tr_peermsgs * msgs )
|
||||
{
|
||||
const int req_max = msgs->maxActiveRequests;
|
||||
return msgs->clientWillAskFor.count >= req_max;
|
||||
return msgs->clientWillAskFor.len >= (size_t)req_max;
|
||||
}
|
||||
|
||||
tr_addreq_t
|
||||
|
@ -987,11 +862,11 @@ tr_peerMsgsAddRequest( tr_peermsgs * msgs,
|
|||
req.index = index;
|
||||
req.offset = offset;
|
||||
req.length = length;
|
||||
if( reqListFind( &msgs->clientAskedFor, &req ) != -1 ) {
|
||||
if( reqListHas( &msgs->clientAskedFor, &req ) ) {
|
||||
dbgmsg( msgs, "declining because it's a duplicate" );
|
||||
return TR_ADDREQ_DUPLICATE;
|
||||
}
|
||||
if( reqListFind( &msgs->clientWillAskFor, &req ) != -1 ) {
|
||||
if( reqListHas( &msgs->clientWillAskFor, &req ) ) {
|
||||
dbgmsg( msgs, "declining because it's a duplicate" );
|
||||
return TR_ADDREQ_DUPLICATE;
|
||||
}
|
||||
|
@ -1010,7 +885,7 @@ tr_peerMsgsAddRequest( tr_peermsgs * msgs,
|
|||
static void
|
||||
cancelAllRequestsToPeer( tr_peermsgs * msgs, tr_bool sendCancel )
|
||||
{
|
||||
int i;
|
||||
size_t i;
|
||||
struct request_list a = msgs->clientWillAskFor;
|
||||
struct request_list b = msgs->clientAskedFor;
|
||||
dbgmsg( msgs, "cancelling all requests to peer" );
|
||||
|
@ -1018,13 +893,13 @@ cancelAllRequestsToPeer( tr_peermsgs * msgs, tr_bool sendCancel )
|
|||
msgs->clientAskedFor = REQUEST_LIST_INIT;
|
||||
msgs->clientWillAskFor = REQUEST_LIST_INIT;
|
||||
|
||||
for( i=0; i<a.count; ++i )
|
||||
fireCancelledReq( msgs, &a.requests[i] );
|
||||
for( i=0; i<a.len; ++i )
|
||||
fireCancelledReq( msgs, &a.fifo[i] );
|
||||
|
||||
for( i = 0; i < b.count; ++i ) {
|
||||
fireCancelledReq( msgs, &b.requests[i] );
|
||||
for( i = 0; i < b.len; ++i ) {
|
||||
fireCancelledReq( msgs, &b.fifo[i] );
|
||||
if( sendCancel )
|
||||
protocolSendCancel( msgs, &b.requests[i] );
|
||||
protocolSendCancel( msgs, &b.fifo[i] );
|
||||
}
|
||||
|
||||
reqListClear( &a );
|
||||
|
@ -1686,7 +1561,7 @@ clientGotBlock( tr_peermsgs * msgs,
|
|||
}
|
||||
|
||||
dbgmsg( msgs, "peer has %d more blocks we've asked for",
|
||||
msgs->clientAskedFor.count );
|
||||
msgs->clientAskedFor.len );
|
||||
|
||||
/**
|
||||
*** Error checks
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* This file Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.com>
|
||||
*
|
||||
* This file is licensed by the GPL version 2. Works owned by the
|
||||
* Transmission project are granted a special exemption to clause 2(b)
|
||||
* so that the bulk of its code can remain under the MIT license.
|
||||
* This exemption does not extend to derived works not owned by
|
||||
* the Transmission project.
|
||||
*
|
||||
* $Id:$
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "transmission.h"
|
||||
#include "request-list.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int
|
||||
compareRequests( const void * va, const void * vb )
|
||||
{
|
||||
const struct peer_request * a = va;
|
||||
const struct peer_request * b = vb;
|
||||
|
||||
if( a->index != b->index )
|
||||
return a->index < b->index ? -1 : 1;
|
||||
|
||||
if( a->offset != b->offset )
|
||||
return a->offset < b->offset ? -1 : 1;
|
||||
|
||||
if( a->length != b->length )
|
||||
return a->length < b->length ? -1 : 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct request_list REQUEST_LIST_INIT = { 0, 0, NULL, NULL };
|
||||
|
||||
static void
|
||||
reqListReserve( struct request_list * list, size_t max )
|
||||
{
|
||||
if( list->max < max )
|
||||
{
|
||||
list->max = max;
|
||||
list->fifo = tr_renew( struct peer_request, list->fifo, list->max );
|
||||
list->sort = tr_renew( struct peer_request, list->sort, list->max );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
reqListClear( struct request_list * list )
|
||||
{
|
||||
tr_free( list->fifo );
|
||||
tr_free( list->sort );
|
||||
*list = REQUEST_LIST_INIT;
|
||||
}
|
||||
|
||||
void
|
||||
reqListCopy( struct request_list * dest, const struct request_list * src )
|
||||
{
|
||||
dest->len = dest->max = src->len;
|
||||
dest->fifo = tr_memdup( src->fifo, dest->len * sizeof( struct peer_request ) );
|
||||
dest->sort = tr_memdup( src->sort, dest->len * sizeof( struct peer_request ) );
|
||||
}
|
||||
|
||||
static int
|
||||
reqListSortPos( const struct request_list * list,
|
||||
const struct peer_request * req,
|
||||
tr_bool * exactMatch )
|
||||
{
|
||||
return tr_lowerBound( req,
|
||||
list->sort,
|
||||
list->len,
|
||||
sizeof( struct peer_request ),
|
||||
compareRequests,
|
||||
exactMatch );
|
||||
}
|
||||
|
||||
void
|
||||
reqListAppend( struct request_list * list,
|
||||
const struct peer_request * req )
|
||||
{
|
||||
int low;
|
||||
|
||||
reqListReserve( list, list->len + 8 );
|
||||
|
||||
/* append into list->fifo */
|
||||
list->fifo[list->len] = *req;
|
||||
|
||||
/* insert into list->sort */
|
||||
low = reqListSortPos( list, req, NULL );
|
||||
memmove( &list->sort[low+1], &list->sort[low], (list->len-low)*sizeof(struct peer_request) );
|
||||
list->sort[low] = *req;
|
||||
|
||||
++list->len;
|
||||
}
|
||||
|
||||
static tr_bool
|
||||
reqListRemoveFromSorted( struct request_list * list, const struct peer_request * key )
|
||||
{
|
||||
tr_bool found;
|
||||
const int low = reqListSortPos( list, key, &found );
|
||||
if( found )
|
||||
memmove( &list->sort[low], &list->sort[low+1], (list->len-low-1)*sizeof(struct peer_request));
|
||||
return found;
|
||||
}
|
||||
|
||||
static void
|
||||
reqListRemoveNthFromFifo( struct request_list * list, int n )
|
||||
{
|
||||
memmove( &list->fifo[n], &list->fifo[n+1], (list->len-1)*sizeof(struct peer_request));
|
||||
}
|
||||
|
||||
tr_bool
|
||||
reqListPop( struct request_list * list,
|
||||
struct peer_request * setme )
|
||||
{
|
||||
tr_bool success;
|
||||
|
||||
if( !list->len )
|
||||
{
|
||||
success = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
*setme = list->fifo[0];
|
||||
reqListRemoveNthFromFifo( list, 0 );
|
||||
reqListRemoveFromSorted( list, setme );
|
||||
--list->len;
|
||||
success = TRUE;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
tr_bool
|
||||
reqListHas( const struct request_list * list,
|
||||
const struct peer_request * key )
|
||||
{
|
||||
tr_bool exactMatch;
|
||||
reqListSortPos( list, key, &exactMatch );
|
||||
return exactMatch;
|
||||
}
|
||||
|
||||
tr_bool
|
||||
reqListRemove( struct request_list * list,
|
||||
const struct peer_request * key )
|
||||
{
|
||||
tr_bool found = reqListRemoveFromSorted( list, key );
|
||||
|
||||
if( found )
|
||||
{
|
||||
size_t i;
|
||||
for( i=0; i<list->len; ++i )
|
||||
if( !compareRequests( &list->fifo[i], key ) )
|
||||
break;
|
||||
assert( i < list->len );
|
||||
reqListRemoveNthFromFifo( list, i );
|
||||
--list->len;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* This file Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.com>
|
||||
*
|
||||
* This file is licensed by the GPL version 2. Works owned by the
|
||||
* Transmission project are granted a special exemption to clause 2(b)
|
||||
* so that the bulk of its code can remain under the MIT license.
|
||||
* This exemption does not extend to derived works not owned by
|
||||
* the Transmission project.
|
||||
*
|
||||
* $Id:$
|
||||
*/
|
||||
|
||||
#ifndef __TRANSMISSION__
|
||||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#ifndef TR_PEER_REQ_H
|
||||
#define TR_PEER_REQ_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
struct peer_request
|
||||
{
|
||||
uint32_t index;
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
time_t time_requested;
|
||||
};
|
||||
|
||||
struct request_list
|
||||
{
|
||||
size_t len;
|
||||
size_t max;
|
||||
struct peer_request * fifo;
|
||||
struct peer_request * sort;
|
||||
};
|
||||
|
||||
extern const struct request_list REQUEST_LIST_INIT;
|
||||
|
||||
void reqListClear( struct request_list * list );
|
||||
|
||||
void reqListCopy( struct request_list * dest, const struct request_list * src );
|
||||
|
||||
/* O(log(N)) */
|
||||
tr_bool reqListHas( const struct request_list * list, const struct peer_request * key );
|
||||
|
||||
/* O(log(N) + 1) */
|
||||
void reqListAppend( struct request_list * list, const struct peer_request * req );
|
||||
|
||||
/* O(log(N) + 1) */
|
||||
tr_bool reqListPop( struct request_list * list, struct peer_request * setme );
|
||||
|
||||
/* O(N + log(N)) */
|
||||
tr_bool reqListRemove( struct request_list * list, const struct peer_request * key );
|
||||
|
||||
|
||||
#endif
|
|
@ -1287,3 +1287,46 @@ tr_releaseBuffer( struct evbuffer * buf )
|
|||
|
||||
tr_lockUnlock( l );
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
int
|
||||
tr_lowerBound( const void * key,
|
||||
const void * base,
|
||||
size_t nmemb,
|
||||
size_t size,
|
||||
int (* compar)(const void* key, const void* arrayMember),
|
||||
tr_bool * exact_match )
|
||||
{
|
||||
size_t first = 0;
|
||||
|
||||
while( nmemb > 0 )
|
||||
{
|
||||
const size_t half = nmemb / 2;
|
||||
const size_t middle = first + half;
|
||||
const int c = compar( key, ((const char*)base) + size*middle );
|
||||
|
||||
if( c < 0 )
|
||||
{
|
||||
first = middle + 1;
|
||||
nmemb = nmemb - half - 1;
|
||||
}
|
||||
else if( !c )
|
||||
{
|
||||
if( exact_match )
|
||||
*exact_match = TRUE;
|
||||
return middle;
|
||||
}
|
||||
else
|
||||
{
|
||||
nmemb = half;
|
||||
}
|
||||
}
|
||||
|
||||
if( exact_match )
|
||||
*exact_match = FALSE;
|
||||
|
||||
return first;
|
||||
}
|
||||
|
|
|
@ -315,6 +315,15 @@ static inline char* tr_strdup( const void * in )
|
|||
return tr_strndup( in, in ? strlen( (const char*)in ) : 0 );
|
||||
}
|
||||
|
||||
/* @brief same argument list as bsearch() */
|
||||
int tr_lowerBound( const void * key,
|
||||
const void * base,
|
||||
size_t nmemb,
|
||||
size_t size,
|
||||
int (* compar)(const void* key, const void* arrayMember),
|
||||
tr_bool * exact_match );
|
||||
|
||||
|
||||
char* tr_strdup_printf( const char * fmt,
|
||||
... ) TR_GNUC_PRINTF( 1, 2 ) TR_GNUC_MALLOC;
|
||||
|
||||
|
|
Loading…
Reference in New Issue