source: trunk/libtransmission/fdlimit.c @ 2004

Last change on this file since 2004 was 2004, checked in by charles, 15 years ago

uninteresting minor stuff like adding const. this commit is just to reduce the shear between trunk and what lands in the file-selection branch.

  • Property svn:keywords set to Date Rev Author Id
File size: 13.5 KB
Line 
1/******************************************************************************
2 * $Id: fdlimit.c 2004 2007-06-09 15:36:46Z charles $
3 *
4 * Copyright (c) 2005-2006 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include "transmission.h"
26
27#define TR_MAX_OPEN_FILES 16 /* That is, real files, not sockets */
28#define TR_RESERVED_FDS   16 /* Number of sockets reserved for
29                                connections to trackers */
30
31/***********************************************************************
32 * Structures
33 **********************************************************************/
34typedef struct tr_openFile_s
35{
36    char       folder[MAX_PATH_LENGTH];
37    char       name[MAX_PATH_LENGTH];
38    int        file;
39    int        write;
40
41#define STATUS_INVALID 1
42#define STATUS_UNUSED  2
43#define STATUS_USED    4
44#define STATUS_CLOSING 8
45    int        status;
46
47    uint64_t   date;
48}
49tr_openFile_t;
50
51typedef struct tr_fd_s
52{
53    tr_lock_t       lock;
54    tr_cond_t       cond;
55   
56    int             reserved;
57
58    int             normal;
59    int             normalMax;
60
61    tr_openFile_t   open[TR_MAX_OPEN_FILES];
62}
63tr_fd_t;
64
65static tr_fd_t * gFd = NULL;
66
67/***********************************************************************
68 * Local prototypes
69 **********************************************************************/
70static int  OpenFile( int i, const char * folder, const char * name, int write );
71static void CloseFile( int i );
72
73
74/***********************************************************************
75 * tr_fdInit
76 **********************************************************************/
77void tr_fdInit()
78{
79    int i, j, s[4096];
80
81    if( gFd )
82    {
83        tr_err( "tr_fdInit was called before!" );
84        return;
85    }
86
87    gFd = calloc( 1, sizeof( tr_fd_t ) );
88
89    /* Init lock and cond */
90    tr_lockInit( &gFd->lock );
91    tr_condInit( &gFd->cond );
92
93    /* Detect the maximum number of open files or sockets */
94    for( i = 0; i < 4096; i++ )
95    {
96        if( ( s[i] = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
97        {
98            break;
99        }
100    }
101    for( j = 0; j < i; j++ )
102    {
103#ifdef BEOS_NETSERVER
104            closesocket( s[j] );
105#else
106            close( s[j] );
107#endif
108    }
109
110    tr_dbg( "%d usable file descriptors", i );
111
112    gFd->reserved  = 0;
113    gFd->normal    = 0;
114
115    gFd->normalMax = i - TR_RESERVED_FDS - 10;
116        /* To be safe, in case the UI needs to write a preferences file
117           or something */
118
119    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
120    {
121        gFd->open[i].status = STATUS_INVALID;
122    }
123}
124
125/***********************************************************************
126 * tr_fdFileOpen
127 **********************************************************************/
128int tr_fdFileOpen( const char * folder, const char * name, int write )
129{
130    int i, winner, ret;
131    uint64_t date;
132
133    tr_lockLock( &gFd->lock );
134
135    /* Is it already open? */
136    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
137    {
138        if( gFd->open[i].status & STATUS_INVALID ||
139            strcmp( folder, gFd->open[i].folder ) ||
140            strcmp( name, gFd->open[i].name ) )
141        {
142            continue;
143        }
144        if( gFd->open[i].status & STATUS_CLOSING )
145        {
146            /* File is being closed by another thread, wait until
147             * it's done before we reopen it */
148            tr_condWait( &gFd->cond, &gFd->lock );
149            i = -1;
150            continue;
151        }
152        if( gFd->open[i].write < write )
153        {
154            /* File is open read-only and needs to be closed then
155             * re-opened read-write */
156            CloseFile( i );
157            continue;
158        }
159        winner = i;
160        goto done;
161    }
162
163    /* Can we open one more file? */
164    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
165    {
166        if( gFd->open[i].status & STATUS_INVALID )
167        {
168            winner = i;
169            goto open;
170        }
171    }
172
173    /* All slots taken - close the oldest currently unused file */
174    for( ;; )
175    {
176        date   = tr_date() + 1;
177        winner = -1;
178
179        for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
180        {
181            if( !( gFd->open[i].status & STATUS_UNUSED ) )
182            {
183                continue;
184            }
185            if( gFd->open[i].date < date )
186            {
187                winner = i;
188                date   = gFd->open[i].date;
189            }
190        }
191
192        if( winner >= 0 )
193        {
194            CloseFile( winner );
195            goto open;
196        }
197
198        /* All used! Wait a bit and try again */
199        tr_condWait( &gFd->cond, &gFd->lock );
200    }
201
202open:
203    if( ( ret = OpenFile( winner, folder, name, write ) ) )
204    {
205        tr_lockUnlock( &gFd->lock );
206        return ret;
207    }
208    snprintf( gFd->open[winner].folder, MAX_PATH_LENGTH, "%s", folder );
209    snprintf( gFd->open[winner].name, MAX_PATH_LENGTH, "%s", name );
210    gFd->open[winner].write = write;
211
212done:
213    gFd->open[winner].status = STATUS_USED;
214    gFd->open[winner].date   = tr_date();
215    tr_lockUnlock( &gFd->lock );
216   
217    return gFd->open[winner].file;
218}
219
220/***********************************************************************
221 * tr_fdFileRelease
222 **********************************************************************/
223void tr_fdFileRelease( int file )
224{
225    int i;
226    tr_lockLock( &gFd->lock );
227
228    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
229    {
230        if( gFd->open[i].file == file )
231        {
232            gFd->open[i].status = STATUS_UNUSED;
233            break;
234        }
235    }
236   
237    tr_condSignal( &gFd->cond );
238    tr_lockUnlock( &gFd->lock );
239}
240
241/***********************************************************************
242 * tr_fdFileClose
243 **********************************************************************/
244void tr_fdFileClose( const char * folder, const char * name )
245{
246    int i;
247
248    tr_lockLock( &gFd->lock );
249
250    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
251    {
252        if( gFd->open[i].status & STATUS_INVALID )
253        {
254            continue;
255        }
256        if( !strcmp( folder, gFd->open[i].folder ) &&
257            !strcmp( name, gFd->open[i].name ) )
258        {
259            CloseFile( i );
260        }
261    }
262
263    tr_lockUnlock( &gFd->lock );
264}
265
266
267/***********************************************************************
268 * Sockets
269 **********************************************************************/
270typedef struct
271{
272    int socket;
273    int priority;
274}
275tr_socket_t;
276
277/* Remember the priority of every socket we open, so that we can keep
278 * track of how many reserved file descriptors we are using */
279static tr_socket_t * gSockets = NULL;
280static int gSocketsSize = 0;
281static int gSocketsCount = 0;
282static void SocketSetPriority( int s, int priority )
283{
284    if( gSocketsSize < 1 )
285    {
286        gSocketsSize = 256;
287        gSockets = malloc( gSocketsSize * sizeof( tr_socket_t ) );
288    }
289    if( gSocketsSize <= gSocketsCount )
290    {
291        gSocketsSize *= 2;
292        gSockets = realloc( gSockets, gSocketsSize * sizeof( tr_socket_t ) );
293    }
294    gSockets[gSocketsCount].socket = s;
295    gSockets[gSocketsCount].priority = priority;
296    gSocketsCount++;
297}
298static int SocketGetPriority( int s )
299{
300    int i, ret;
301    for( i = 0; i < gSocketsCount; i++ )
302        if( gSockets[i].socket == s )
303            break;
304    if( i >= gSocketsCount )
305    {
306        tr_err( "could not find that socket (%d)!", s );
307        return -1;
308    }
309    ret = gSockets[i].priority;
310    gSocketsCount--;
311    memmove( &gSockets[i], &gSockets[i+1],
312            ( gSocketsCount - i ) * sizeof( tr_socket_t ) );
313    return ret;
314}
315
316/***********************************************************************
317 * tr_fdSocketCreate
318 **********************************************************************/
319int tr_fdSocketCreate( int type, int priority )
320{
321    int s = -1;
322
323    tr_lockLock( &gFd->lock );
324    if( ( priority && gFd->reserved < TR_RESERVED_FDS ) ||
325        ( !priority && gFd->normal < gFd->normalMax ) )
326    {
327       if( ( s = socket( AF_INET, type, 0 ) ) < 0 )
328       {
329           tr_err( "Could not create socket (%s)", strerror( errno ) );
330       }
331    }
332    if( s > -1 )
333    {
334        SocketSetPriority( s, priority );
335        if( priority )
336            gFd->reserved++;
337        else
338            gFd->normal++;
339    }
340    tr_lockUnlock( &gFd->lock );
341
342    return s;
343}
344
345int tr_fdSocketAccept( int b, struct in_addr * addr, in_port_t * port )
346{
347    int s = -1;
348    unsigned len;
349    struct sockaddr_in sock;
350
351    tr_lockLock( &gFd->lock );
352    if( gFd->normal < gFd->normalMax )
353    {
354        len = sizeof( sock );
355        s = accept( b, (struct sockaddr *) &sock, &len );
356    }
357    if( s > -1 )
358    {
359        SocketSetPriority( s, 0 );
360        if( NULL != addr )
361        {
362            *addr = sock.sin_addr;
363        }
364        if( NULL != port )
365        {
366            *port = sock.sin_port;
367        }
368        gFd->normal++;
369    }
370    tr_lockUnlock( &gFd->lock );
371
372    return s;
373}
374
375/***********************************************************************
376 * tr_fdSocketClose
377 **********************************************************************/
378void tr_fdSocketClose( int s )
379{
380    tr_lockLock( &gFd->lock );
381#ifdef BEOS_NETSERVER
382    closesocket( s );
383#else
384    close( s );
385#endif
386    if( SocketGetPriority( s ) )
387        gFd->reserved--;
388    else
389        gFd->normal--;
390    tr_lockUnlock( &gFd->lock );
391}
392
393/***********************************************************************
394 * tr_fdClose
395 **********************************************************************/
396void tr_fdClose()
397{
398    tr_lockClose( &gFd->lock );
399    tr_condClose( &gFd->cond );
400    free( gFd );
401}
402
403
404/***********************************************************************
405 * Local functions
406 **********************************************************************/
407
408/***********************************************************************
409 * CheckFolder
410 ***********************************************************************
411 *
412 **********************************************************************/
413static int OpenFile( int i, const char * folder, const char * name, int write )
414{
415    tr_openFile_t * file = &gFd->open[i];
416    struct stat sb;
417    char path[MAX_PATH_LENGTH];
418    int ret;
419
420    tr_dbg( "Opening %s in %s (%d)", name, folder, write );
421
422    /* Make sure the parent folder exists */
423    if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) )
424    {
425        return TR_ERROR_IO_PARENT;
426    }
427
428    snprintf( path, sizeof(path), "%s/%s", folder, name );
429
430    /* Create subfolders, if any */
431    if( write )
432    {
433        char * p = path + strlen( folder ) + 1;
434        char * s;
435
436        while( ( s = strchr( p, '/' ) ) )
437        {
438            *s = '\0';
439            if( stat( path, &sb ) )
440            {
441                if( mkdir( path, 0777 ) )
442                {
443                    ret = tr_ioErrorFromErrno();
444                    tr_err( "Could not create folder '%s'", path );
445                    return ret;
446                }
447            }
448            else
449            {
450                if( !S_ISDIR( sb.st_mode ) )
451                {
452                    tr_err( "Is not a folder: '%s'", path );
453                    return TR_ERROR_IO_OTHER;
454                }
455            }
456            *s = '/';
457            p = s + 1;
458        }
459    }
460
461    /* Now try to really open the file */
462    file->file = open( path, write ? ( O_RDWR | O_CREAT ) : O_RDONLY, 0666 );
463    if( file->file < 0 )
464    {
465        ret = tr_ioErrorFromErrno();
466        tr_err( "Could not open %s in %s (%d, %d)", name, folder, write, ret );
467        return ret;
468    }
469
470    return TR_OK;
471}
472
473/***********************************************************************
474 * CloseFile
475 ***********************************************************************
476 * We first mark it as closing then release the lock while doing so,
477 * because close() may take same time and we don't want to block other
478 * threads.
479 **********************************************************************/
480static void CloseFile( int i )
481{
482    tr_openFile_t * file = &gFd->open[i];
483
484    /* If it's already being closed by another thread, just wait till
485     * it is done */
486    while( file->status & STATUS_CLOSING )
487    {
488        tr_condWait( &gFd->cond, &gFd->lock );
489    }
490    if( file->status & STATUS_INVALID )
491    {
492        return;
493    }
494
495    /* Nobody is closing it already, so let's do it */
496    if( file->status & STATUS_USED )
497    {
498        tr_err( "CloseFile: closing a file that's being used!" );
499    }
500    tr_dbg( "Closing %s in %s (%d)", file->name, file->folder, file->write );
501    file->status = STATUS_CLOSING;
502    tr_lockUnlock( &gFd->lock );
503    close( file->file );
504    tr_lockLock( &gFd->lock );
505    file->status = STATUS_INVALID;
506    tr_condSignal( &gFd->cond );
507}
508
Note: See TracBrowser for help on using the repository browser.