298 lines
7.2 KiB
C
298 lines
7.2 KiB
C
|
/******************************************************************************
|
||
|
* $Id$
|
||
|
*
|
||
|
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
* copy of this software and associated documentation files (the "Software"),
|
||
|
* to deal in the Software without restriction, including without limitation
|
||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||
|
* Software is furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in
|
||
|
* all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||
|
* DEALINGS IN THE SOFTWARE.
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#include "transmission.h"
|
||
|
|
||
|
#define TR_MAX_OPEN_FILES 16 /* That is, real files, not sockets */
|
||
|
#define TR_RESERVED_FDS 16 /* Number of sockets reserved for
|
||
|
connections to trackers */
|
||
|
|
||
|
typedef struct tr_openFile_s
|
||
|
{
|
||
|
char path[MAX_PATH_LENGTH];
|
||
|
int file;
|
||
|
|
||
|
#define STATUS_INVALID 1
|
||
|
#define STATUS_UNUSED 2
|
||
|
#define STATUS_USED 4
|
||
|
#define STATUS_CLOSING 8
|
||
|
int status;
|
||
|
|
||
|
uint64_t date;
|
||
|
|
||
|
} tr_openFile_t;
|
||
|
|
||
|
struct tr_fd_s
|
||
|
{
|
||
|
tr_lock_t lock;
|
||
|
|
||
|
int reserved;
|
||
|
|
||
|
int normal;
|
||
|
int normalMax;
|
||
|
|
||
|
tr_openFile_t open[TR_MAX_OPEN_FILES];
|
||
|
};
|
||
|
|
||
|
/***********************************************************************
|
||
|
* tr_fdInit
|
||
|
**********************************************************************/
|
||
|
tr_fd_t * tr_fdInit()
|
||
|
{
|
||
|
tr_fd_t * f;
|
||
|
int i, j, s[4096];
|
||
|
|
||
|
f = calloc( sizeof( tr_fd_t ), 1 );
|
||
|
|
||
|
/* Init lock */
|
||
|
tr_lockInit( &f->lock );
|
||
|
|
||
|
/* Detect the maximum number of open files or sockets */
|
||
|
for( i = 0; i < 4096; i++ )
|
||
|
{
|
||
|
if( ( s[i] = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
for( j = 0; j < i; j++ )
|
||
|
{
|
||
|
tr_netClose( s[j] );
|
||
|
}
|
||
|
|
||
|
tr_dbg( "%d usable file descriptors", i );
|
||
|
|
||
|
f->reserved = 0;
|
||
|
f->normal = 0;
|
||
|
|
||
|
f->normalMax = i - TR_RESERVED_FDS - 10;
|
||
|
/* To be safe, in case the UI needs to write a preferences file
|
||
|
or something */
|
||
|
|
||
|
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
||
|
{
|
||
|
f->open[i].status = STATUS_INVALID;
|
||
|
}
|
||
|
|
||
|
return f;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
* tr_fdFileOpen
|
||
|
**********************************************************************/
|
||
|
int tr_fdFileOpen( tr_fd_t * f, char * path )
|
||
|
{
|
||
|
int i, winner;
|
||
|
uint64_t date;
|
||
|
|
||
|
tr_lockLock( &f->lock );
|
||
|
|
||
|
/* Is it already open? */
|
||
|
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
||
|
{
|
||
|
if( f->open[i].status > STATUS_INVALID &&
|
||
|
!strcmp( path, f->open[i].path ) )
|
||
|
{
|
||
|
if( f->open[i].status & STATUS_CLOSING )
|
||
|
{
|
||
|
/* Wait until the file is closed */
|
||
|
tr_lockUnlock( &f->lock );
|
||
|
tr_wait( 10 );
|
||
|
tr_lockLock( &f->lock );
|
||
|
i = -1;
|
||
|
continue;
|
||
|
}
|
||
|
winner = i;
|
||
|
goto done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Can we open one more file? */
|
||
|
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
||
|
{
|
||
|
if( f->open[i].status & STATUS_INVALID )
|
||
|
{
|
||
|
winner = i;
|
||
|
goto open;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for( ;; )
|
||
|
{
|
||
|
/* Close the oldest currently unused file */
|
||
|
date = tr_date() + 1;
|
||
|
winner = -1;
|
||
|
|
||
|
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
||
|
{
|
||
|
if( !( f->open[i].status & STATUS_UNUSED ) )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
if( f->open[i].date < date )
|
||
|
{
|
||
|
winner = i;
|
||
|
date = f->open[i].date;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( winner >= 0 )
|
||
|
{
|
||
|
/* Close the file: we mark it as closing then release the
|
||
|
lock while doing so, because close may take same time
|
||
|
and we don't want to block other threads */
|
||
|
tr_dbg( "Closing %s", f->open[winner].path );
|
||
|
f->open[winner].status = STATUS_CLOSING;
|
||
|
tr_lockUnlock( &f->lock );
|
||
|
close( f->open[winner].file );
|
||
|
tr_lockLock( &f->lock );
|
||
|
goto open;
|
||
|
}
|
||
|
|
||
|
/* All used! Wait a bit and try again */
|
||
|
tr_lockUnlock( &f->lock );
|
||
|
tr_wait( 10 );
|
||
|
tr_lockLock( &f->lock );
|
||
|
}
|
||
|
|
||
|
open:
|
||
|
tr_dbg( "Opening %s", path );
|
||
|
snprintf( f->open[winner].path, MAX_PATH_LENGTH, "%s", path );
|
||
|
f->open[winner].file = open( path, O_RDWR, 0 );
|
||
|
|
||
|
done:
|
||
|
f->open[winner].status = STATUS_USED;
|
||
|
f->open[winner].date = tr_date();
|
||
|
tr_lockUnlock( &f->lock );
|
||
|
|
||
|
return f->open[winner].file;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
* tr_fdFileRelease
|
||
|
**********************************************************************/
|
||
|
void tr_fdFileRelease( tr_fd_t * f, int file )
|
||
|
{
|
||
|
int i;
|
||
|
tr_lockLock( &f->lock );
|
||
|
|
||
|
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
||
|
{
|
||
|
if( f->open[i].file == file )
|
||
|
{
|
||
|
f->open[i].status = STATUS_UNUSED;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tr_lockUnlock( &f->lock );
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
* tr_fdFileClose
|
||
|
**********************************************************************/
|
||
|
void tr_fdFileClose( tr_fd_t * f, char * path )
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
tr_lockLock( &f->lock );
|
||
|
|
||
|
/* Is it already open? */
|
||
|
for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
|
||
|
{
|
||
|
if( f->open[i].status & STATUS_INVALID )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
if( !strcmp( path, f->open[i].path ) )
|
||
|
{
|
||
|
tr_dbg( "Closing %s", path );
|
||
|
close( f->open[i].file );
|
||
|
f->open[i].status = STATUS_INVALID;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tr_lockUnlock( &f->lock );
|
||
|
}
|
||
|
|
||
|
int tr_fdSocketWillCreate( tr_fd_t * f, int reserved )
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
tr_lockLock( &f->lock );
|
||
|
|
||
|
if( reserved )
|
||
|
{
|
||
|
if( f->reserved < TR_RESERVED_FDS )
|
||
|
{
|
||
|
ret = 0;
|
||
|
(f->reserved)++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ret = 1;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( f->normal < f->normalMax )
|
||
|
{
|
||
|
ret = 0;
|
||
|
(f->normal)++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ret = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tr_lockUnlock( &f->lock );
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void tr_fdSocketClosed( tr_fd_t * f, int reserved )
|
||
|
{
|
||
|
tr_lockLock( &f->lock );
|
||
|
|
||
|
if( reserved )
|
||
|
{
|
||
|
(f->reserved)--;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
(f->normal)--;
|
||
|
}
|
||
|
|
||
|
tr_lockUnlock( &f->lock );
|
||
|
}
|
||
|
|
||
|
void tr_fdClose( tr_fd_t * f )
|
||
|
{
|
||
|
tr_lockClose( &f->lock );
|
||
|
free( f );
|
||
|
}
|
||
|
|