296 lines
7.2 KiB
C
296 lines
7.2 KiB
C
/******************************************************************************
|
|
* Copyright (c) 2005 Eric Petit
|
|
*
|
|
* 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];
|
|
FILE * 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
|
|
**********************************************************************/
|
|
FILE * 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 fclose 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 );
|
|
fclose( 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 = fopen( path, "r+" );
|
|
|
|
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, FILE * 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 );
|
|
fclose( 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 );
|
|
}
|
|
|