source: trunk/libtransmission/fdlimit.c @ 2328

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

clean up #includes a bit.

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