(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:
Charles Kerr 2009-01-04 16:29:44 +00:00
parent a0390c6111
commit 51e68d6e56
6 changed files with 306 additions and 153 deletions

View File

@ -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}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;