source: trunk/libtransmission/fdlimit.c @ 1356

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

Merge io branch into trunk

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