Thread safety improvements. Also, stopping/starting/rechecking/etc. torrents no longer blocks the calling thread. Since this a big commit, it will probably create some short-term pain via new bugs.

This commit is contained in:
Charles Kerr 2007-06-26 18:45:03 +00:00
parent 21cf51e3f7
commit 7e09d157cd
18 changed files with 1221 additions and 1414 deletions

View File

@ -154,7 +154,7 @@ int main( int argc, char ** argv )
}
/* Open and parse torrent file */
if( !( tor = tr_torrentInit( h, torrentPath, NULL, 0, &error ) ) )
if( !( tor = tr_torrentInit( h, torrentPath, ".", NULL, 0, &error ) ) )
{
printf( "Failed opening torrent file `%s'\n", torrentPath );
tr_close( h );
@ -227,7 +227,6 @@ int main( int argc, char ** argv )
tr_natTraversalEnable( h, natTraversal );
tr_torrentSetFolder( tor, "." );
tr_torrentStart( tor );
for( ;; )
@ -246,11 +245,7 @@ int main( int argc, char ** argv )
s = tr_torrentStat( tor );
if( s->status & TR_STATUS_PAUSE )
{
break;
}
else if( s->status & TR_STATUS_CHECK_WAIT )
if( s->status & TR_STATUS_CHECK_WAIT )
{
chars = snprintf( string, sizeof string,
"Waiting to check files... %.2f %%", 100.0 * s->percentDone );

View File

@ -956,14 +956,8 @@ recheckTorrentForeach (GtkTreeModel * model,
gpointer data UNUSED)
{
TrTorrent * gtor = NULL;
int status = 0;
tr_torrent_t * tor;
gtk_tree_model_get( model, iter, MC_TORRENT, &gtor, MC_STAT, &status, -1 );
tor = tr_torrent_handle( gtor );
if( status & TR_STATUS_ACTIVE )
tr_torrentStop( tor );
tr_torrentRemoveFastResume( tor );
tr_torrentStart( tor );
gtk_tree_model_get( model, iter, MC_TORRENT, &gtor, -1 );
tr_torrentRecheck( tr_torrent_handle( gtor ) );
g_object_unref( G_OBJECT( gtor ) );
}

View File

@ -31,9 +31,6 @@
#include "bencode.h"
#include "transmission.h"
/* XXX */
#define TR_WANT_TORRENT_PRIVATE
#include "conf.h"
#include "tr_core.h"
#include "tr_prefs.h"
@ -55,10 +52,6 @@ tr_core_marshal_data( GClosure * closure, GValue * ret, guint count,
const GValue * vals, gpointer hint, gpointer marshal );
static void
tr_core_dispose( GObject * obj );
static int
tr_core_check_torrents( TrCore * self );
static int
tr_core_check_zombies( TrCore * self );
static void
tr_core_insert( TrCore * self, TrTorrent * tor );
static void
@ -238,7 +231,6 @@ tr_core_init( GTypeInstance * instance, gpointer g_class SHUTUP )
self->model = GTK_TREE_MODEL( store );
self->handle = tr_init( "gtk" );
self->zombies = NULL;
self->nextid = 1;
self->quitting = FALSE;
self->disposed = FALSE;
@ -250,7 +242,6 @@ tr_core_dispose( GObject * obj )
TrCore * self = (TrCore *) obj;
GObjectClass * parent;
GtkTreeIter iter;
GList * ii;
TrTorrent * tor;
if( self->disposed )
@ -273,17 +264,6 @@ tr_core_dispose( GObject * obj )
while( gtk_tree_model_iter_next( self->model, &iter ) );
g_object_unref( self->model );
/* sever and unref all remaining zombie torrents */
if( NULL != self->zombies )
{
for( ii = g_list_first( self->zombies ); NULL != ii; ii = ii->next )
{
tr_torrent_sever( ii->data );
g_object_unref( ii->data );
}
g_list_free( self->zombies );
}
#ifdef REFDBG
fprintf( stderr, "core %p dead\n", self );
#endif
@ -322,29 +302,22 @@ void
tr_core_shutdown( TrCore * self )
{
GtkTreeIter iter;
TrTorrent * tor;
TR_IS_CORE( self );
if( self->disposed )
{
return;
}
g_assert( !self->quitting );
self->quitting = TRUE;
/* try to stop all the torrents nicely */
if( gtk_tree_model_get_iter_first( self->model, &iter) )
{
do
{
gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
tr_torrent_stop( tor );
g_object_unref( tor );
}
while( gtk_tree_model_iter_next( self->model, &iter ) );
}
if ( gtk_tree_model_get_iter_first( self->model, &iter) ) do {
TrTorrent * tor;
gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
tr_torrent_sever( tor );
g_object_unref( tor );
} while( gtk_list_store_remove( GTK_LIST_STORE(self->model), &iter ) );
/* shut down nat traversal */
tr_natTraversalEnable( self->handle, 0 );
@ -353,78 +326,25 @@ tr_core_shutdown( TrCore * self )
gboolean
tr_core_quiescent( TrCore * self )
{
tr_handle_status_t * hstat;
const tr_handle_status_t * hstat;
TR_IS_CORE( self );
g_assert( self->quitting );
if( self->disposed )
{
return TRUE;
}
if( 0 < tr_core_check_torrents( self ) )
{
if ( tr_torrentCount( self->handle ) != 0 )
return FALSE;
}
hstat = tr_handleStatus( self->handle );
return TR_NAT_TRAVERSAL_DISABLED == hstat->natTraversalStatus;
}
int
tr_core_check_torrents( TrCore * self )
{
GtkTreeIter iter;
tr_stat_t * st;
int count;
TrTorrent * tor;
g_assert( !self->disposed && self->quitting );
count = 0;
if( gtk_tree_model_get_iter_first( self->model, &iter) )
{
do
{
gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
st = tr_torrent_stat( tor );
if( !( TR_STATUS_PAUSE & st->status ) )
{
count++;
}
g_object_unref( tor );
}
while( gtk_tree_model_iter_next( self->model, &iter ) );
}
count += tr_core_check_zombies( self );
return count;
}
int
tr_core_check_zombies( TrCore * self )
{
GList * ii, * next;
tr_stat_t * st;
for( ii = g_list_first( self->zombies ); NULL != ii; ii = next )
{
next = ii->next;
st = tr_torrent_stat( ii->data );
if( TR_STATUS_PAUSE & st->status )
{
tr_torrent_sever( ii->data );
g_object_unref( ii->data );
/* XXX is this safe to do? */
self->zombies = g_list_delete_link( self->zombies, ii );
}
}
return g_list_length( self->zombies );
return gtk_tree_model_iter_n_children( self->model, NULL );
}
void
@ -666,29 +586,16 @@ void
tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter )
{
TrTorrent * tor;
tr_stat_t * st;
TR_IS_CORE( self );
gtk_tree_model_get( self->model, iter, MC_TORRENT, &tor, -1 );
gtk_list_store_remove( GTK_LIST_STORE( self->model ), iter );
if( TR_FLAG_SAVE & tr_torrent_info( tor )->flags )
{
tr_torrentRemoveSaved( tr_torrent_handle( tor ) );
}
st = tr_torrent_stat( tor );
if( TR_STATUS_ACTIVE & st->status )
{
tr_torrentStop( tr_torrent_handle( tor ) );
self->zombies = g_list_append( self->zombies, tor );
}
else
{
tr_torrent_sever( tor );
g_object_unref( tor );
}
if( TR_FLAG_SAVE & tr_torrent_info( tor )->flags )
tr_torrentRemoveSaved( tr_torrent_handle( tor ) );
tr_torrent_sever( tor );
}
void
@ -715,7 +622,7 @@ tr_core_update( TrCore * self )
{
GtkTreeIter iter;
TrTorrent * tor;
tr_stat_t * st;
const tr_stat_t * st;
TR_IS_CORE( self );
@ -752,11 +659,6 @@ tr_core_update( TrCore * self )
}
while( gtk_tree_model_iter_next( self->model, &iter ) );
}
if( !self->quitting )
{
tr_core_check_zombies( self );
}
}
void

View File

@ -60,7 +60,6 @@ struct _TrCore
GObject parent;
GtkTreeModel * model;
tr_handle_t * handle;
GList * zombies;
int nextid;
gboolean quitting;
gboolean disposed;

View File

@ -31,9 +31,6 @@
#include "transmission.h"
#include "bencode.h"
/* XXX */
#define TR_WANT_TORRENT_PRIVATE
#include "tr_prefs.h"
#include "tr_torrent.h"
#include "util.h"
@ -41,7 +38,6 @@
enum {
TR_TORRENT_HANDLE = 1,
TR_TORRENT_DIR,
TR_TORRENT_PAUSED,
};
static void
@ -58,8 +54,6 @@ static void
tr_torrent_dispose(GObject *obj);
static void
tr_torrent_set_folder(TrTorrent *tor);
static gboolean
tr_torrent_paused(TrTorrent *tor);
static gpointer
tracker_boxed_fake_copy( gpointer boxed )
@ -127,11 +121,6 @@ tr_torrent_class_init(gpointer g_class, gpointer g_class_data SHUTUP) {
"Directory to download files to", NULL,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
g_object_class_install_property(gobject_class, TR_TORRENT_DIR, pspec);
pspec = g_param_spec_boolean("paused", "Paused",
"Is the torrent paused or running", TRUE,
G_PARAM_READWRITE);
g_object_class_install_property(gobject_class, TR_TORRENT_PAUSED, pspec);
}
static void
@ -143,6 +132,7 @@ tr_torrent_init(GTypeInstance *instance, gpointer g_class SHUTUP) {
#endif
self->handle = NULL;
self->lastStatTime = 0;
self->dir = NULL;
self->delfile = NULL;
self->severed = FALSE;
@ -174,12 +164,6 @@ tr_torrent_set_property(GObject *object, guint property_id,
if(NULL != self->handle && NULL != self->dir)
tr_torrent_set_folder(self);
break;
case TR_TORRENT_PAUSED:
g_assert(NULL != self->handle);
if(tr_torrent_paused(self) != g_value_get_boolean(value))
(g_value_get_boolean(value) ? tr_torrentStop : tr_torrentStart)
(self->handle);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
@ -202,9 +186,6 @@ tr_torrent_get_property(GObject *object, guint property_id,
g_value_set_string(value, (NULL != self->dir ? self->dir :
tr_torrentGetFolder(self->handle)));
break;
case TR_TORRENT_PAUSED:
g_value_set_boolean(value, tr_torrent_paused(self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
@ -225,9 +206,7 @@ tr_torrent_dispose(GObject *obj) {
#endif
if( !self->severed )
{
tr_torrent_sever( self );
}
g_free (self->delfile);
g_free (self->dir);
@ -241,47 +220,39 @@ tr_torrent_sever( TrTorrent * self )
{
g_return_if_fail (TR_IS_TORRENT( self ));
if( self->severed )
{
return;
}
#ifdef REFDBG
fprintf( stderr, "torrent %p sever\n", self );
#endif
if( NULL == self->handle )
if( !self->severed )
{
self->severed = TRUE;
return;
}
if( !tr_torrent_paused( self ) )
{
tr_torrentStop( self->handle );
if( self->handle )
tr_torrentClose( self->handle );
}
tr_torrentClose( self->handle );
self->severed = TRUE;
}
tr_torrent_t *
tr_torrent_handle(TrTorrent *tor) {
TR_IS_TORRENT(tor);
tr_torrent_handle(TrTorrent *tor)
{
g_assert( TR_IS_TORRENT(tor) );
if(tor->severed)
return NULL;
return tor->handle;
return tor->severed ? NULL : tor->handle;
}
tr_stat_t *
tr_torrent_stat(TrTorrent *tor) {
TR_IS_TORRENT(tor);
const tr_stat_t *
tr_torrent_stat(TrTorrent *tor)
{
const time_t now = time( NULL );
if(tor->severed)
return NULL;
g_assert( TR_IS_TORRENT(tor) );
return tr_torrentStat(tor->handle);
if( tor->severed )
return NULL;
if( tor->lastStatTime != now ) {
tor->lastStatTime = now;
tor->stat = *tr_torrentStat( tor->handle );
}
return &tor->stat;
}
tr_info_t *
@ -299,12 +270,8 @@ tr_torrent_start( TrTorrent * self )
{
TR_IS_TORRENT( self );
if( self->severed || !tr_torrent_paused( self ) )
{
return;
}
tr_torrentStart( self->handle );
if( !self->severed )
tr_torrentStart( self->handle );
}
void
@ -312,31 +279,19 @@ tr_torrent_stop( TrTorrent * self )
{
TR_IS_TORRENT( self );
if( self->severed || tr_torrent_paused( self ) )
{
return;
}
tr_torrentStop( self->handle );
if( !self->severed )
tr_torrentStop( self->handle );
}
static TrTorrent *
maketorrent( tr_torrent_t * handle,
const char * dir,
gboolean paused )
maketorrent( tr_torrent_t * handle )
{
TrTorrent * tor;
tr_torrentDisablePex( handle,
!tr_prefs_get_bool_with_default( PREF_ID_PEX ) );
tor = g_object_new( TR_TORRENT_TYPE,
"torrent-handle", handle,
"download-directory", dir,
NULL);
g_object_set( tor, "paused", paused, NULL );
return tor;
return g_object_new( TR_TORRENT_TYPE,
"torrent-handle", handle,
NULL);
}
TrTorrent *
@ -345,16 +300,19 @@ tr_torrent_new( tr_handle_t * back, const char *torrent, const char *dir,
{
TrTorrent *ret;
tr_torrent_t *handle;
int errcode, flag;
int errcode, flags;
g_assert(NULL != dir);
*err = NULL;
flag = ( TR_TOR_COPY == act || TR_TOR_MOVE == act ? TR_FLAG_SAVE : 0 );
errcode = -1;
handle = tr_torrentInit( back, torrent, NULL, flag, &errcode );
flags = ( TR_TOR_COPY == act || TR_TOR_MOVE == act ? TR_FLAG_SAVE : 0 );
if( paused )
flags |= TR_FLAG_PAUSED;
handle = tr_torrentInit( back, torrent, dir, NULL, flags, &errcode );
if(NULL == handle) {
switch(errcode) {
@ -371,7 +329,7 @@ tr_torrent_new( tr_handle_t * back, const char *torrent, const char *dir,
return NULL;
}
ret = maketorrent( handle, dir, paused );
ret = maketorrent( handle );
if( TR_TOR_MOVE == act )
ret->delfile = g_strdup(torrent);
@ -385,14 +343,18 @@ tr_torrent_new_with_data( tr_handle_t * back, uint8_t * data, size_t size,
{
tr_torrent_t * handle;
int errcode;
int flags;
g_assert( NULL != dir );
*err = NULL;
flags = TR_FLAG_SAVE;
if( paused )
flags |= TR_FLAG_PAUSED;
errcode = -1;
handle = tr_torrentInitData( back, data, size, NULL, TR_FLAG_SAVE,
&errcode );
handle = tr_torrentInitData( back, data, size, dir, NULL, flags, &errcode );
if( NULL == handle )
{
@ -411,7 +373,7 @@ tr_torrent_new_with_data( tr_handle_t * back, uint8_t * data, size_t size,
return NULL;
}
return maketorrent( handle, dir, paused );
return maketorrent( handle );
}
TrTorrent *
@ -421,6 +383,7 @@ tr_torrent_new_with_state( tr_handle_t * back, benc_val_t * state,
TrTorrent * ret;
tr_torrent_t * handle;
int ii, errcode;
int flags;
benc_val_t *name, *data;
char *torrent, *hash, *dir;
gboolean paused = FALSE;
@ -463,10 +426,14 @@ tr_torrent_new_with_state( tr_handle_t * back, benc_val_t * state,
(NULL == torrent && NULL == hash) || NULL == dir)
return NULL;
flags = 0;
if( forcedpause )
flags |= TR_FLAG_PAUSED;
if( NULL != hash )
handle = tr_torrentInitSaved(back, hash, 0, &errcode);
handle = tr_torrentInitSaved(back, hash, dir, flags, &errcode);
else
handle = tr_torrentInit(back, torrent, NULL, 0, &errcode);
handle = tr_torrentInit(back, torrent, dir, NULL, flags, &errcode);
if(NULL == handle) {
torrent = ( NULL == hash ? torrent : hash );
@ -484,7 +451,7 @@ tr_torrent_new_with_state( tr_handle_t * back, benc_val_t * state,
return NULL;
}
ret = maketorrent( handle, dir, paused || forcedpause );
ret = maketorrent( handle );
ret->ul_cap = ul_cap;
ret->ul_cap_enabled = ul_cap_enabled;
ret->dl_cap = dl_cap;
@ -526,8 +493,10 @@ tr_torrent_get_state( TrTorrent * tor, benc_val_t * state )
}
tr_bencInitStr( tr_bencDictAdd( state, "dir" ),
tr_torrentGetFolder( tor->handle ), -1, 1 );
#if 0
tr_bencInitInt( tr_bencDictAdd( state, "paused" ),
tr_torrent_paused( tor ) ? 1 : 0 );
#endif
tr_bencInitInt( tr_bencDictAdd( state, "ul-cap-speed" ),
tor->ul_cap );
@ -576,13 +545,6 @@ tr_torrent_set_folder(TrTorrent *tor) {
}
}
static gboolean
tr_torrent_paused(TrTorrent *tor) {
tr_stat_t *st = tr_torrentStat(tor->handle);
return (TR_STATUS_INACTIVE & st->status ? TRUE : FALSE);
}
extern void tr_setUseCustomUpload( tr_torrent_t * tor, int limit );
extern void tr_setUseCustomDownload( tr_torrent_t * tor, int limit );
@ -622,7 +584,7 @@ tr_torrent_set_download_cap_enabled ( TrTorrent *gtor, gboolean b ) {
void
tr_torrent_check_seeding_cap ( TrTorrent *gtor) {
tr_stat_t * st = tr_torrent_stat( gtor );
const tr_stat_t * st = tr_torrent_stat( gtor );
if ((gtor->seeding_cap_enabled) && (st->ratio >= gtor->seeding_cap))
tr_torrent_stop (gtor);
}
@ -642,62 +604,59 @@ tr_torrent_status_str ( TrTorrent * gtor )
{
char * top = 0;
tr_stat_t * st = tr_torrent_stat( gtor );
const tr_stat_t * st = tr_torrent_stat( gtor );
const int tpeers = MAX (st->peersTotal, 0);
const int upeers = MAX (st->peersUploading, 0);
const int eta = st->eta;
const double prog = st->percentDone * 100.0; /* [0...100] */
const int status = st->status;
if( TR_STATUS_CHECK_WAIT & status )
switch( st->status )
{
top = g_strdup_printf( _("Waiting to check existing files (%.1f%%)"), prog );
}
else if( TR_STATUS_CHECK & status )
{
top = g_strdup_printf( _("Checking existing files (%.1f%%)"), prog );
}
else if( TR_STATUS_DOWNLOAD & status )
{
if( 0 > eta )
{
top = g_strdup_printf( _("Stalled (%.1f%%)"), prog );
}
else
{
char * timestr = readabletime(eta);
top = g_strdup_printf( _("Finishing in %s (%.1f%%)"),
timestr, prog );
g_free(timestr);
}
}
else if( TR_STATUS_SEED & status )
{
top = g_strdup_printf(
ngettext( "Seeding, uploading to %d of %d peer",
"Seeding, uploading to %d of %d peers", tpeers ),
upeers, tpeers );
}
else if( TR_STATUS_DONE & status )
{
top = g_strdup_printf(
ngettext( "Uploading to %d of %d peer",
"Uploading to %d of %d peers", tpeers ),
upeers, tpeers );
}
else if( TR_STATUS_STOPPING & status )
{
top = g_strdup( _("Stopping...") );
}
else if( TR_STATUS_PAUSE & status )
{
top = g_strdup_printf( _("Stopped (%.1f%%)"), prog );
}
else
{
top = g_strdup( "" );
g_assert_not_reached();
case TR_STATUS_CHECK_WAIT:
top = g_strdup_printf( _("Waiting to check existing files (%.1f%%)"), prog );
break;
case TR_STATUS_CHECK:
top = g_strdup_printf( _("Checking existing files (%.1f%%)"), prog );
break;
case TR_STATUS_DOWNLOAD:
if( eta < 0 )
top = g_strdup_printf( _("Stalled (%.1f%%)"), prog );
else {
char * timestr = readabletime(eta);
top = g_strdup_printf( _("Finishing in %s (%.1f%%)"), timestr, prog );
g_free(timestr);
}
break;
case TR_STATUS_DONE:
top = g_strdup_printf(
ngettext( "Uploading to %d of %d peer",
"Uploading to %d of %d peers", tpeers ),
upeers, tpeers );
break;
case TR_STATUS_SEED:
top = g_strdup_printf(
ngettext( "Seeding, uploading to %d of %d peer",
"Seeding, uploading to %d of %d peers", tpeers ),
upeers, tpeers );
break;
case TR_STATUS_STOPPING:
top = g_strdup( _("Stopping...") );
break;
case TR_STATUS_STOPPED:
top = g_strdup( _("Stopped") );
break;
default:
top = g_strdup_printf("Unrecognized state: %d", st->status );
break;
}
return top;

View File

@ -58,6 +58,8 @@ struct _TrTorrent {
tr_torrent_t *handle;
char *dir;
char *delfile;
tr_stat_t stat;
time_t lastStatTime;
/* FIXME: hm, are these heavyweight enough to deserve their own properties? */
gboolean severed;
@ -80,7 +82,7 @@ tr_torrent_get_type(void);
tr_torrent_t *
tr_torrent_handle(TrTorrent *tor);
tr_stat_t *
const tr_stat_t *
tr_torrent_stat(TrTorrent *tor);
tr_info_t *
@ -112,8 +114,6 @@ tr_torrent_set_seeding_cap_ratio ( TrTorrent*, gdouble ratio );
void
tr_torrent_set_seeding_cap_enabled ( TrTorrent*, gboolean );
#ifdef TR_WANT_TORRENT_PRIVATE
TrTorrent *
tr_torrent_new( tr_handle_t * handle, const char * path, const char * dir,
enum tr_torrent_action act, gboolean paused, char ** err);
@ -135,6 +135,4 @@ tr_torrent_state_saved(TrTorrent *tor);
void
tr_torrent_sever( TrTorrent * tor );
#endif /* TR_WANT_TORRENT_PRIVATE */
#endif

View File

@ -145,7 +145,7 @@ void tr_chokingPulse( tr_choking_t * c )
peersTotalCount = 0;
for( tor = c->h->torrentList; tor; tor = tor->next )
{
tr_lockLock( &tor->lock );
tr_torrentWriterLock( tor );
peersTotalCount += tor->peerCount;
}
@ -310,9 +310,7 @@ void tr_chokingPulse( tr_choking_t * c )
/* Unlock all torrents */
for( tor = c->h->torrentList; tor; tor = tor->next )
{
tr_lockUnlock( &tor->lock );
}
tr_torrentWriterUnlock( tor );
tr_lockUnlock( &c->lock );
}

View File

@ -211,7 +211,7 @@ fastResumeLoadProgress( const tr_torrent_t * tor,
tr_time_t * curMTimes = getMTimes( tor, &n );
const tr_time_t * oldMTimes = (const tr_time_t *) walk;
for( i=0; i<n; ++i ) {
if ( !curMTimes[i] || ( curMTimes[i]!=oldMTimes[i] ) ) {
if ( curMTimes[i]!=oldMTimes[i] ) {
const tr_file_t * file = &tor->info.files[i];
tr_dbg( "File '%s' mtimes differ-- flagging pieces [%d..%d] for recheck",
file->name, file->firstPiece, file->lastPiece);

View File

@ -14,8 +14,6 @@
struct tr_io_s
{
tr_torrent_t * tor;
tr_bitfield_t * uncheckedPieces;
};
#include "fastresume.h"
@ -135,10 +133,6 @@ readOrWritePiece ( tr_torrent_t * tor,
assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
assert( buflen <= (size_t) tr_pieceSize( pieceIndex ) );
/* Release the torrent lock so the UI can still update itself if
this blocks for a while */
tr_lockUnlock( &tor->lock );
findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset );
while( buflen && !ret )
@ -158,8 +152,6 @@ readOrWritePiece ( tr_torrent_t * tor,
fileOffset = 0;
}
tr_lockLock( &tor->lock );
return ret;
}
@ -217,37 +209,46 @@ checkPiece ( tr_torrent_t * tor, int pieceIndex )
return ret;
}
static void
checkFiles( tr_io_t * io )
int
tr_ioCheckFiles( tr_torrent_t * tor, int mode )
{
int i;
tr_torrent_t * tor = io->tor;
tr_bitfield_t * uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
tr_bitfieldClear( io->uncheckedPieces );
tr_cpReset( tor->completion );
if( fastResumeLoad( io->tor, io->uncheckedPieces ) )
tr_bitfieldAddRange( io->uncheckedPieces, 0, tor->info.pieceCount-1 );
tr_bitfieldClear( uncheckedPieces );
if( !tr_bitfieldIsEmpty( io->uncheckedPieces ) )
tr_inf( "Rechecking portions of \"%s\"", tor->info.name );
if( (mode==TR_RECHECK_FORCE) || fastResumeLoad( tor, uncheckedPieces ) )
tr_bitfieldAddRange( uncheckedPieces, 0, tor->info.pieceCount-1 );
if( tr_bitfieldIsEmpty( uncheckedPieces ) ) {
tr_bitfieldFree( uncheckedPieces );
return TR_OK;
}
if( mode == TR_RECHECK_FAST ) {
tr_bitfieldFree( uncheckedPieces );
return TR_ERROR_IO_OTHER;
}
tr_inf( "Verifying some pieces of \"%s\"", tor->info.name );
for( i=0; i<tor->info.pieceCount; ++i )
{
if( tor->status & TR_STATUS_STOPPING )
break;
if( !tr_bitfieldHas( io->uncheckedPieces, i ) )
if( !tr_bitfieldHas( uncheckedPieces, i ) )
continue;
tr_dbg ( "Checking piece %d because it's not in fast-resume", i );
if( !checkPiece( tor, i ) )
tr_cpPieceAdd( tor->completion, i );
else
tr_cpPieceRem( tor->completion, i );
tr_torrentSetHasPiece( tor, i, !checkPiece( tor, i ) );
tr_bitfieldRem( io->uncheckedPieces, i );
tr_bitfieldRem( uncheckedPieces, i );
}
fastResumeSave( tor );
tr_bitfieldFree( uncheckedPieces );
return TR_OK;
}
/****
@ -255,49 +256,52 @@ checkFiles( tr_io_t * io )
****/
tr_io_t*
tr_ioInit( tr_torrent_t * tor )
tr_ioInitFast( tr_torrent_t * tor )
{
tr_io_t * io = calloc( 1, sizeof( tr_io_t ) );
io->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
tr_io_t * io = tr_calloc( 1, sizeof( tr_io_t ) );
io->tor = tor;
checkFiles( io );
if( tr_ioCheckFiles( tor, TR_RECHECK_FAST ) )
{
tr_free( io );
io = NULL;
}
return io;
}
void
tr_ioSync( tr_io_t * io )
{
int i;
const tr_info_t * info = &io->tor->info;
if( io != NULL )
{
int i;
const tr_info_t * info = &io->tor->info;
for( i=0; i<info->fileCount; ++i )
tr_fdFileClose( io->tor->destination, info->files[i].name );
for( i=0; i<info->fileCount; ++i )
tr_fdFileClose( io->tor->destination, info->files[i].name );
if( tr_bitfieldIsEmpty( io->uncheckedPieces ) )
fastResumeSave( io->tor );
}
}
void
tr_ioClose( tr_io_t * io )
{
tr_ioSync( io );
tr_bitfieldFree( io->uncheckedPieces );
free( io );
if( io != NULL )
{
tr_ioSync( io );
tr_free( io );
}
}
/* try to load the fast resume file */
void
int
tr_ioLoadResume( tr_torrent_t * tor )
{
tr_io_t * io = calloc( 1, sizeof( tr_io_t ) );
io->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
io->tor = tor;
fastResumeLoad( tor, io->uncheckedPieces );
tor->ioLoaded = 1;
tr_bitfieldFree( io->uncheckedPieces );
free( io );
return tr_ioCheckFiles ( tor, TR_RECHECK_FAST );
}
void tr_ioRemoveResume( tr_torrent_t * tor )

View File

@ -27,10 +27,19 @@
typedef struct tr_io_s tr_io_t;
void tr_ioLoadResume ( tr_torrent_t * );
void tr_ioRemoveResume( tr_torrent_t * tor );
int tr_ioLoadResume ( tr_torrent_t * );
void tr_ioRemoveResume( tr_torrent_t * );
tr_io_t * tr_ioInit ( tr_torrent_t * );
enum
{
TR_RECHECK_FAST, /* only try the fast resume, even if it's incomplete */
TR_RECHECK_FORCE /* ignore the fast resume data; recheck from disk */
};
int tr_ioCheckFiles ( tr_torrent_t *, int recheckMode );
tr_io_t * tr_ioInitFast ( tr_torrent_t * );
/***********************************************************************
* tr_ioRead, tr_ioWrite

View File

@ -157,10 +157,28 @@ typedef enum { TR_NET_OK, TR_NET_ERROR, TR_NET_WAIT } tr_tristate_t;
#define FALSE 0
#endif
void tr_torrentResetTransferStats( tr_torrent_t * );
int tr_torrentAddCompact( tr_torrent_t * tor, int from,
uint8_t * buf, int count );
int tr_torrentAttachPeer( tr_torrent_t * tor, tr_peer_t * peer );
void tr_torrentSetHasPiece( tr_torrent_t * tor, int pieceIndex, int has );
void tr_torrentReaderLock ( const tr_torrent_t * );
void tr_torrentReaderUnlock ( const tr_torrent_t * );
void tr_torrentWriterLock ( tr_torrent_t * );
void tr_torrentWriterUnlock ( tr_torrent_t * );
typedef enum
{
TR_RUN_CHECKING = (1<<0), /* checking files' checksums */
TR_RUN_RUNNING = (1<<1), /* seeding or leeching */
TR_RUN_STOPPING = (1<<2), /* stopping */
TR_RUN_STOPPED = (1<<3) /* stopped */
}
run_status_t;
struct tr_torrent_s
{
tr_handle_t * handle;
@ -172,7 +190,6 @@ struct tr_torrent_s
tr_ratecontrol_t * download;
tr_ratecontrol_t * swarmspeed;
int status;
int error;
char errorString[128];
int hasChangedState;
@ -194,10 +211,12 @@ struct tr_torrent_s
tr_completion_t * completion;
volatile char die;
volatile char dieFlag;
volatile char recheckFlag;
run_status_t runStatus;
cp_status_t cpStatus;
tr_thread_t thread;
tr_lock_t lock;
tr_cond_t cond;
tr_rwlock_t lock;
tr_tracker_t * tracker;
tr_io_t * io;
@ -219,7 +238,6 @@ struct tr_torrent_s
tr_stat_t stats[2];
int statCur;
tr_torrent_t * prev;
tr_torrent_t * next;
};

View File

@ -59,7 +59,7 @@ static const int SWIFT_INITIAL_CREDIT = 64 * 1024; /* 64 KiB */
* on largesse by uniformly distributing free credit to
* all of our peers. This too helps prevent gridlock.
*/
static const double SWIFT_LARGESSE = 0.05; /* 5% of our UL */
static const double SWIFT_LARGESSE = 0.10; /* 10% of our UL */
/**
* How frequently to extend largesse-based credit
@ -450,7 +450,7 @@ int tr_peerPulse( tr_peer_t * peer )
/* Disconnect if seeder and torrent is seeding */
if( ( peer->progress >= 1.0 )
&& ( peer->tor->status & (TR_STATUS_SEED|TR_STATUS_DONE) ) )
&& ( peer->tor->cpStatus != TR_CP_INCOMPLETE ) )
{
return TR_ERROR;
}
@ -836,7 +836,7 @@ tr_torrentSwiftPulse ( tr_torrent_t * tor )
int deadbeatCount = 0;
tr_peer_t ** deadbeats;
tr_lockLock( &tor->lock );
tr_torrentWriterLock( tor );
deadbeats = tr_calloc( tor->peerCount, sizeof(tr_peer_t*) );
for( i=0; i<tor->peerCount; ++i ) {
@ -864,7 +864,7 @@ tr_torrentSwiftPulse ( tr_torrent_t * tor )
free( deadbeats );
tr_lockUnlock( &tor->lock );
tr_torrentWriterUnlock( tor );
}
void
tr_swiftPulse( tr_handle_t * h )

View File

@ -200,6 +200,7 @@ tr_getTorrentsDirectory( void )
static void ThreadFunc( void * _t )
{
tr_thread_t * t = _t;
char* name = tr_strdup( t->name );
#ifdef SYS_BEOS
/* This is required because on BeOS, SIGINT is sent to each thread,
@ -207,9 +208,10 @@ static void ThreadFunc( void * _t )
signal( SIGINT, SIG_IGN );
#endif
tr_dbg( "Thread '%s' started", t->name );
tr_dbg( "Thread '%s' started", name );
t->func( t->arg );
tr_dbg( "Thread '%s' exited", t->name );
tr_dbg( "Thread '%s' exited", name );
tr_free( name );
}
void tr_threadCreate( tr_thread_t * t,
@ -241,7 +243,7 @@ void tr_threadJoin( tr_thread_t * t )
pthread_join( t->thread, NULL );
#endif
tr_dbg( "Thread '%s' joined", t->name );
free( t->name );
tr_free( t->name );
t->name = NULL;
t->func = NULL;
}
@ -270,6 +272,7 @@ int tr_lockTryLock( tr_lock_t * l )
#ifdef SYS_BEOS
#error how is this done in beos
#else
/* success on zero! */
return pthread_mutex_trylock( l );
#endif
}

View File

@ -320,9 +320,9 @@ static void SetPublicPort( tr_shared_t * s, int port )
for( tor = h->torrentList; tor; tor = tor->next )
{
tr_lockLock( &tor->lock );
tr_torrentWriterLock( tor );
tor->publicPort = port;
tr_lockUnlock( &tor->lock );
tr_torrentWriterUnlock( tor );
}
}
@ -403,22 +403,21 @@ static void DispatchPeers( tr_shared_t * s )
for( tor = h->torrentList; tor; tor = tor->next )
{
tr_lockLock( &tor->lock );
if( tor->status & TR_STATUS_INACTIVE )
tr_torrentWriterLock( tor );
if( tor->cpStatus != TR_RUN_RUNNING )
{
tr_lockUnlock( &tor->lock );
tr_torrentWriterUnlock( tor );
continue;
}
if( 0 == memcmp( tor->info.hash, hash,
SHA_DIGEST_LENGTH ) )
if( !memcmp( tor->info.hash, hash, SHA_DIGEST_LENGTH ) )
{
/* Found it! */
tr_torrentAttachPeer( tor, s->peers[ii] );
tr_lockUnlock( &tor->lock );
tr_torrentWriterUnlock( tor );
goto removePeer;
}
tr_lockUnlock( &tor->lock );
tr_torrentWriterUnlock( tor );
}
/* Couldn't find a torrent, we probably removed it */

File diff suppressed because it is too large Load Diff

View File

@ -474,6 +474,9 @@ void tr_trackerCompleted( tr_tracker_t * tc )
void tr_trackerStopped( tr_tracker_t * tc )
{
if( tc == NULL )
return;
/* If we are already sending a query at the moment, we need to
reconnect */
killHttp( &tc->http );
@ -491,6 +494,9 @@ void tr_trackerClose( tr_tracker_t * tc )
size_t ii;
struct tclist * dead;
if( tc == NULL )
return;
killHttp( &tc->http );
killHttp( &tc->httpScrape );
@ -522,11 +528,8 @@ static tr_http_t * getQuery( tr_tracker_t * tc )
if( tc->started )
{
event = "&event=started";
tor->downloadedPrev += tor->downloadedCur;
tor->downloadedCur = 0;
tor->uploadedPrev += tor->uploadedCur;
tor->uploadedCur = 0;
tr_torrentResetTransferStats( tor );
if( shouldChangePort( tc ) )
{
@ -832,7 +835,7 @@ nodict:
if( tc->stopped )
{
tor->status = TR_STATUS_STOPPED;
tr_torrentStop( tor );
tc->stopped = 0;
}
else if( shouldChangePort( tc ) )

View File

@ -145,11 +145,11 @@ void tr_torrentRates( tr_handle_t * h, float * dl, float * ul )
tr_sharedLock( h->shared );
for( tor = h->torrentList; tor; tor = tor->next )
{
tr_lockLock( &tor->lock );
if( tor->status & TR_STATUS_DOWNLOAD )
tr_torrentReaderLock( tor );
if( tor->cpStatus == TR_CP_INCOMPLETE )
*dl += tr_rcRate( tor->download );
*ul += tr_rcRate( tor->upload );
tr_lockUnlock( &tor->lock );
tr_torrentReaderUnlock( tor );
}
tr_sharedUnlock( h->shared );
}

View File

@ -213,8 +213,6 @@ enum
typedef int8_t tr_priority_t;
void tr_torrentInitFilePieces( tr_torrent_t * tor );
/* priorities should be an array of tor->info.fileCount bytes,
* each holding a value of TR_PRI_NORMAL, _HIGH, _LOW, or _DND. */
void tr_torrentSetFilePriorities ( tr_torrent_t *, const tr_priority_t * priorities );
@ -265,7 +263,9 @@ void tr_close( tr_handle_t * );
#define TR_EUNSUPPORTED 2
#define TR_EDUPLICATE 3
#define TR_EOTHER 666
tr_torrent_t * tr_torrentInit( tr_handle_t *, const char * path,
tr_torrent_t * tr_torrentInit( tr_handle_t *,
const char * path,
const char * destination,
uint8_t * hash, int flags, int * error );
/***********************************************************************
@ -274,9 +274,10 @@ tr_torrent_t * tr_torrentInit( tr_handle_t *, const char * path,
* Like tr_torrentInit, except the actual torrent data is passed in
* instead of the filename.
**********************************************************************/
tr_torrent_t * tr_torrentInitData( tr_handle_t *, uint8_t * data,
size_t size, uint8_t * hash,
int flags, int * error );
tr_torrent_t * tr_torrentInitData( tr_handle_t *,
uint8_t * data, size_t size,
const char * destination,
uint8_t * hash, int flags, int * error );
/***********************************************************************
* tr_torrentInitSaved
@ -285,7 +286,9 @@ tr_torrent_t * tr_torrentInitData( tr_handle_t *, uint8_t * data,
* the hash string of a saved torrent file instead of a filename. There
* are currently no valid flags for this function.
**********************************************************************/
tr_torrent_t * tr_torrentInitSaved( tr_handle_t *, const char * hashStr,
tr_torrent_t * tr_torrentInitSaved( tr_handle_t *,
const char * hashStr,
const char * destination,
int flags, int * error );
/***********************************************************************
@ -317,9 +320,7 @@ tr_info_t * tr_torrentInfo( tr_torrent_t * );
int tr_torrentScrape( tr_torrent_t *, int * s, int * l, int * d );
void tr_torrentSetFolder( tr_torrent_t *, const char * );
char * tr_torrentGetFolder( tr_torrent_t * );
int tr_torrentDuplicateDownload( tr_torrent_t * tor );
const char * tr_torrentGetFolder( const tr_torrent_t * );
/***********************************************************************
* tr_torrentStart
@ -377,7 +378,7 @@ tr_stat_t * tr_torrentStat( tr_torrent_t * );
* tr_torrentPeers
***********************************************************************/
typedef struct tr_peer_stat_s tr_peer_stat_t;
tr_peer_stat_t * tr_torrentPeers( tr_torrent_t *, int * peerCount );
tr_peer_stat_t * tr_torrentPeers( const tr_torrent_t *, int * peerCount );
void tr_torrentPeersFree( tr_peer_stat_t *, int peerCount );
/***********************************************************************
@ -388,9 +389,9 @@ void tr_torrentPeersFree( tr_peer_stat_t *, int peerCount );
* to either -1 if we have the piece, otherwise it is set to the number
* of connected peers who have the piece.
**********************************************************************/
void tr_torrentAvailability( tr_torrent_t *, int8_t * tab, int size );
void tr_torrentAvailability( const tr_torrent_t *, int8_t * tab, int size );
void tr_torrentAmountFinished( tr_torrent_t * tor, float * tab, int size );
void tr_torrentAmountFinished( const tr_torrent_t * tor, float * tab, int size );
/***********************************************************************
* tr_torrentCompletion
@ -414,13 +415,13 @@ uint64_t tr_torrentFileBytesCompleted( const tr_torrent_t *, int fileIndex );
**********************************************************************/
void tr_torrentRemoveSaved( tr_torrent_t * );
void tr_torrentRemoveFastResume( tr_torrent_t * tor );
void tr_torrentRecheck( tr_torrent_t * );
/***********************************************************************
* tr_torrentClose
***********************************************************************
* Frees memory allocated by tr_torrentInit. If the torrent was running,
* you must call tr_torrentStop() before closing it.
* it is stopped first.
**********************************************************************/
void tr_torrentClose( tr_torrent_t * );
@ -459,6 +460,7 @@ struct tr_info_s
/* Flags */
#define TR_FLAG_SAVE 0x01 /* save a copy of the torrent file */
#define TR_FLAG_PRIVATE 0x02 /* do not share information for this torrent */
#define TR_FLAG_PAUSED 0x04 /* don't start the torrent when adding it */
int flags;
/* Tracker info */
@ -488,32 +490,38 @@ struct tr_info_s
typedef enum
{
TR_CP_COMPLETE, /* has every piece */
TR_CP_DONE, /* has all the pieces but the DND ones */
TR_CP_INCOMPLETE /* doesn't have all the desired pieces */
TR_CP_INCOMPLETE, /* doesn't have all the desired pieces */
TR_CP_DONE, /* has all the pieces but the DND ones */
TR_CP_COMPLETE /* has every piece */
}
cp_status_t;
typedef enum
{
TR_STATUS_CHECK_WAIT = (1<<0), /* Waiting in queue to check files */
TR_STATUS_CHECK = (1<<1), /* Checking files */
TR_STATUS_DOWNLOAD = (1<<2), /* Downloading */
TR_STATUS_DONE = (1<<3), /* not at 100% so can't tell the tracker
we're a seeder, but due to DND files
there's nothing we want right now */
TR_STATUS_SEED = (1<<4), /* Seeding */
TR_STATUS_STOPPING = (1<<5), /* Sending 'stopped' to the tracker */
TR_STATUS_STOPPED = (1<<6) /* Sent 'stopped' but thread still
running (for internal use only) */
}
torrent_status_t;
#define TR_STATUS_ACTIVE \
(TR_STATUS_CHECK_WAIT|TR_STATUS_CHECK|TR_STATUS_DOWNLOAD|TR_STATUS_DONE|TR_STATUS_SEED)
#define TR_STATUS_INACTIVE \
(TR_STATUS_STOPPING|TR_STATUS_STOPPED)
/***********************************************************************
* tr_stat_s
**********************************************************************/
struct tr_stat_s
{
#define TR_STATUS_CHECK_WAIT (1<<0) /* Waiting in queue to check files */
#define TR_STATUS_CHECK (1<<1) /* Checking files */
#define TR_STATUS_DOWNLOAD (1<<2) /* Downloading */
#define TR_STATUS_DONE (1<<3) /* not at 100% so can't tell the tracker
we're a seeder, but due to DND files
there's nothing we want right now */
#define TR_STATUS_SEED (1<<4) /* Seeding */
#define TR_STATUS_STOPPING (1<<5) /* Sending 'stopped' to the tracker */
#define TR_STATUS_STOPPED (1<<6) /* Sent 'stopped' but thread still
running (for internal use only) */
#define TR_STATUS_PAUSE (1<<7) /* Paused */
#define TR_STATUS_ACTIVE (TR_STATUS_CHECK_WAIT|TR_STATUS_CHECK|TR_STATUS_DOWNLOAD|TR_STATUS_DONE|TR_STATUS_SEED)
#define TR_STATUS_INACTIVE (TR_STATUS_STOPPING|TR_STATUS_STOPPED|TR_STATUS_PAUSE)
int status;
torrent_status_t status;
cp_status_t cpStatus;
int error;