source: trunk/libtransmission/fdlimit.c @ 1420

Last change on this file since 1420 was 1420, checked in by titer, 15 years ago

Officially give up on making libT reentrant, and simplify our code instead

  • Property svn:keywords set to Date Rev Author Id
File size: 11.7 KB
Line 
1/******************************************************************************
2 * $Id: fdlimit.c 1420 2007-01-21 07:16:18Z titer $
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  ErrorFromErrno();
71static int  OpenFile( int i, char * folder, char * name, int write );
72static void CloseFile( int i );
73
74
75/***********************************************************************
76 * tr_fdInit
77 **********************************************************************/
78void tr_fdInit()
79{
80    int i, j, s[4096];
81
82    if( gFd )
83    {
84        tr_err( "tr_fdInit was called before!" );
85        return;
86    }
87
88    gFd = calloc( sizeof( tr_fd_t ), 1 );
89
90    /* Init lock and cond */
91    tr_lockInit( &gFd->lock );
92    tr_condInit( &gFd->cond );
93
94    /* Detect the maximum number of open files or sockets */
95    for( i = 0; i < 4096; i++ )
96    {
97        if( ( s[i] = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
98        {
99            break;
100        }
101    }
102    for( j = 0; j < i; j++ )
103    {
104        tr_netClose( s[j] );
105    }
106
107    tr_dbg( "%d usable file descriptors", i );
108
109    gFd->reserved  = 0;
110    gFd->normal    = 0;
111
112    gFd->normalMax = i - TR_RESERVED_FDS - 10;
113        /* To be safe, in case the UI needs to write a preferences file
114           or something */
115
116    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
117    {
118        gFd->open[i].status = STATUS_INVALID;
119    }
120}
121
122/***********************************************************************
123 * tr_fdFileOpen
124 **********************************************************************/
125int tr_fdFileOpen( char * folder, char * name, int write )
126{
127    int i, winner, ret;
128    uint64_t date;
129
130    tr_lockLock( &gFd->lock );
131
132    /* Is it already open? */
133    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
134    {
135        if( gFd->open[i].status & STATUS_INVALID ||
136            strcmp( folder, gFd->open[i].folder ) ||
137            strcmp( name, gFd->open[i].name ) )
138        {
139            continue;
140        }
141        if( gFd->open[i].status & STATUS_CLOSING )
142        {
143            /* File is being closed by another thread, wait until
144             * it's done before we reopen it */
145            tr_condWait( &gFd->cond, &gFd->lock );
146            i = -1;
147            continue;
148        }
149        if( gFd->open[i].write < write )
150        {
151            /* File is open read-only and needs to be closed then
152             * re-opened read-write */
153            CloseFile( i );
154            continue;
155        }
156        winner = i;
157        goto done;
158    }
159
160    /* Can we open one more file? */
161    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
162    {
163        if( gFd->open[i].status & STATUS_INVALID )
164        {
165            winner = i;
166            goto open;
167        }
168    }
169
170    /* All slots taken - close the oldest currently unused file */
171    for( ;; )
172    {
173        date   = tr_date() + 1;
174        winner = -1;
175
176        for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
177        {
178            if( !( gFd->open[i].status & STATUS_UNUSED ) )
179            {
180                continue;
181            }
182            if( gFd->open[i].date < date )
183            {
184                winner = i;
185                date   = gFd->open[i].date;
186            }
187        }
188
189        if( winner >= 0 )
190        {
191            CloseFile( winner );
192            goto open;
193        }
194
195        /* All used! Wait a bit and try again */
196        tr_condWait( &gFd->cond, &gFd->lock );
197    }
198
199open:
200    if( ( ret = OpenFile( winner, folder, name, write ) ) )
201    {
202        tr_lockUnlock( &gFd->lock );
203        return ret;
204    }
205    snprintf( gFd->open[winner].folder, MAX_PATH_LENGTH, "%s", folder );
206    snprintf( gFd->open[winner].name, MAX_PATH_LENGTH, "%s", name );
207    gFd->open[winner].write = write;
208
209done:
210    gFd->open[winner].status = STATUS_USED;
211    gFd->open[winner].date   = tr_date();
212    tr_lockUnlock( &gFd->lock );
213   
214    return gFd->open[winner].file;
215}
216
217/***********************************************************************
218 * tr_fdFileRelease
219 **********************************************************************/
220void tr_fdFileRelease( int file )
221{
222    int i;
223    tr_lockLock( &gFd->lock );
224
225    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
226    {
227        if( gFd->open[i].file == file )
228        {
229            gFd->open[i].status = STATUS_UNUSED;
230            break;
231        }
232    }
233   
234    tr_condSignal( &gFd->cond );
235    tr_lockUnlock( &gFd->lock );
236}
237
238/***********************************************************************
239 * tr_fdFileClose
240 **********************************************************************/
241void tr_fdFileClose( char * folder, char * name )
242{
243    int i;
244
245    tr_lockLock( &gFd->lock );
246
247    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
248    {
249        if( gFd->open[i].status & STATUS_INVALID )
250        {
251            continue;
252        }
253        if( !strcmp( folder, gFd->open[i].folder ) &&
254            !strcmp( name, gFd->open[i].name ) )
255        {
256            CloseFile( i );
257        }
258    }
259
260    tr_lockUnlock( &gFd->lock );
261}
262
263/***********************************************************************
264 * tr_fdSocketWillCreate
265 **********************************************************************/
266int tr_fdSocketWillCreate( int reserved )
267{
268    int ret;
269
270    tr_lockLock( &gFd->lock );
271
272    if( reserved )
273    {
274        if( gFd->reserved < TR_RESERVED_FDS )
275        {
276            ret = 0;
277            (gFd->reserved)++;
278        }
279        else
280        {
281            ret = 1;
282        }
283    }
284    else
285    {
286        if( gFd->normal < gFd->normalMax )
287        {
288            ret = 0;
289            (gFd->normal)++;
290        }
291        else
292        {
293            ret = 1;
294        }
295    }
296
297    tr_lockUnlock( &gFd->lock );
298
299    return ret;
300}
301
302/***********************************************************************
303 * tr_fdSocketClosed
304 **********************************************************************/
305void tr_fdSocketClosed( int reserved )
306{
307    tr_lockLock( &gFd->lock );
308
309    if( reserved )
310    {
311        (gFd->reserved)--;
312    }
313    else
314    {
315        (gFd->normal)--;
316    }
317
318    tr_lockUnlock( &gFd->lock );
319}
320
321/***********************************************************************
322 * tr_fdClose
323 **********************************************************************/
324void tr_fdClose()
325{
326    tr_lockClose( &gFd->lock );
327    tr_condClose( &gFd->cond );
328    free( gFd );
329}
330
331
332/***********************************************************************
333 * Local functions
334 **********************************************************************/
335
336/***********************************************************************
337 * ErrorFromErrno
338 **********************************************************************/
339static int ErrorFromErrno()
340{
341    if( errno == EACCES || errno == EROFS )
342        return TR_ERROR_IO_PERMISSIONS;
343    return TR_ERROR_IO_OTHER;
344}
345
346/***********************************************************************
347 * CheckFolder
348 ***********************************************************************
349 *
350 **********************************************************************/
351static int OpenFile( int i, char * folder, char * name, int write )
352{
353    tr_openFile_t * file = &gFd->open[i];
354    struct stat sb;
355    char * path;
356
357    tr_dbg( "Opening %s in %s (%d)", name, folder, write );
358
359    /* Make sure the parent folder exists */
360    if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) )
361    {
362        return TR_ERROR_IO_PARENT;
363    }
364
365    asprintf( &path, "%s/%s", folder, name );
366
367    /* Create subfolders, if any */
368    if( write )
369    {
370        char * p = path + strlen( folder ) + 1;
371        char * s;
372
373        while( ( s = strchr( p, '/' ) ) )
374        {
375            *s = '\0';
376            if( stat( path, &sb ) )
377            {
378                if( mkdir( path, 0777 ) )
379                {
380                    tr_err( "Could not create folder '%s'", path );
381                    free( path );
382                    return ErrorFromErrno();
383                }
384            }
385            else
386            {
387                if( !S_ISDIR( sb.st_mode ) )
388                {
389                    tr_err( "Is not a folder: '%s'", path );
390                    free( path );
391                    return TR_ERROR_IO_OTHER;
392                }
393            }
394            *s = '/';
395            p = s + 1;
396        }
397    }
398
399    /* Now try to really open the file */
400    file->file = open( path, write ? ( O_RDWR | O_CREAT ) : O_RDONLY, 0666 );
401    free( path );
402
403    if( file->file < 0 )
404    {
405        int ret = ErrorFromErrno();
406        tr_err( "Could not open %s in %s (%d, %d)", name, folder, write, ret );
407        return ret;
408    }
409
410    return TR_OK;
411}
412
413/***********************************************************************
414 * CloseFile
415 ***********************************************************************
416 * We first mark it as closing then release the lock while doing so,
417 * because close() may take same time and we don't want to block other
418 * threads.
419 **********************************************************************/
420static void CloseFile( int i )
421{
422    tr_openFile_t * file = &gFd->open[i];
423
424    /* If it's already being closed by another thread, just wait till
425     * it is done */
426    while( file->status & STATUS_CLOSING )
427    {
428        tr_condWait( &gFd->cond, &gFd->lock );
429    }
430    if( file->status & STATUS_INVALID )
431    {
432        return;
433    }
434
435    /* Nobody is closing it already, so let's do it */
436    if( file->status & STATUS_USED )
437    {
438        tr_err( "CloseFile: closing a file that's being used!" );
439    }
440    tr_dbg( "Closing %s in %s (%d)", file->name, file->folder, file->write );
441    file->status = STATUS_CLOSING;
442    tr_lockUnlock( &gFd->lock );
443    close( file->file );
444    tr_lockLock( &gFd->lock );
445    file->status = STATUS_INVALID;
446    tr_condSignal( &gFd->cond );
447}
448
Note: See TracBrowser for help on using the repository browser.