source: branches/io/libtransmission/fdlimit.c @ 1199

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

Updates implementation (not complete yet)

  • Property svn:keywords set to Date Rev Author Id
File size: 9.9 KB
Line 
1/******************************************************************************
2 * $Id: fdlimit.c 1199 2006-12-13 20:27:00Z 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   
52    int             reserved;
53
54    int             normal;
55    int             normalMax;
56
57    tr_openFile_t   open[TR_MAX_OPEN_FILES];
58};
59
60/***********************************************************************
61 * Local prototypes
62 **********************************************************************/
63static void CloseFile( tr_fd_t * f, int i );
64static int  CheckCanOpen( char * folder, char * name, int write );
65
66
67
68/***********************************************************************
69 * tr_fdInit
70 **********************************************************************/
71tr_fd_t * tr_fdInit()
72{
73    tr_fd_t * f;
74    int i, j, s[4096];
75
76    f = calloc( sizeof( tr_fd_t ), 1 );
77
78    /* Init lock */
79    tr_lockInit( &f->lock );
80
81    /* Detect the maximum number of open files or sockets */
82    for( i = 0; i < 4096; i++ )
83    {
84        if( ( s[i] = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
85        {
86            break;
87        }
88    }
89    for( j = 0; j < i; j++ )
90    {
91        tr_netClose( s[j] );
92    }
93
94    tr_dbg( "%d usable file descriptors", i );
95
96    f->reserved  = 0;
97    f->normal    = 0;
98
99    f->normalMax = i - TR_RESERVED_FDS - 10;
100        /* To be safe, in case the UI needs to write a preferences file
101           or something */
102
103    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
104    {
105        f->open[i].status = STATUS_INVALID;
106    }
107
108    return f;
109}
110
111/***********************************************************************
112 * tr_fdFileOpen
113 **********************************************************************/
114int tr_fdFileOpen( tr_fd_t * f, char * folder, char * name, int write )
115{
116    int i, winner, ret;
117    uint64_t date;
118    char * path;
119
120    tr_lockLock( &f->lock );
121
122    /* Is it already open? */
123    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
124    {
125        if( f->open[i].status & STATUS_INVALID ||
126            strcmp( folder, f->open[i].folder ) ||
127            strcmp( name, f->open[i].name ) )
128        {
129            continue;
130        }
131        if( f->open[i].status & STATUS_CLOSING )
132        {
133            /* File is being closed by another thread, wait until
134             * it's done before we reopen it */
135            tr_lockUnlock( &f->lock );
136            tr_wait( 10 );
137            tr_lockLock( &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    /* We'll need to open it, make sure that we can */
153    if( ( ret = CheckCanOpen( folder, name, write ) ) )
154    {
155        tr_err( "Can not open %s in %s (%d)", name, folder, ret );
156        tr_lockUnlock( &f->lock );
157        return ret;
158    }
159
160    /* Can we open one more file? */
161    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
162    {
163        if( f->open[i].status & STATUS_INVALID )
164        {
165            winner = i;
166            goto open;
167        }
168    }
169
170    for( ;; )
171    {
172        /* Close the oldest currently unused file */
173        date   = tr_date() + 1;
174        winner = -1;
175
176        for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
177        {
178            if( !( f->open[i].status & STATUS_UNUSED ) )
179            {
180                continue;
181            }
182            if( f->open[i].date < date )
183            {
184                winner = i;
185                date   = f->open[i].date;
186            }
187        }
188
189        if( winner >= 0 )
190        {
191            CloseFile( f, winner );
192            goto open;
193        }
194
195        /* All used! Wait a bit and try again */
196        tr_lockUnlock( &f->lock );
197        tr_wait( 10 );
198        tr_lockLock( &f->lock );
199    }
200
201open:
202    tr_dbg( "Opening %s in %s", name, folder );
203    asprintf( &path, "%s/%s", folder, name );
204    f->open[winner].file = open( path, write ? O_RDWR : O_RDONLY, 0 );
205    free( path );
206    if( f->open[winner].file < 0 )
207    {
208        tr_err( "Could not open %s in %s (%d)", name, folder, errno );
209        tr_lockUnlock( &f->lock );
210        return TR_ERROR_IO_OTHER;
211    }
212    snprintf( f->open[winner].folder, MAX_PATH_LENGTH, "%s", folder );
213    snprintf( f->open[winner].name, MAX_PATH_LENGTH, "%s", name );
214    f->open[winner].write = write;
215
216done:
217    f->open[winner].status = STATUS_USED;
218    f->open[winner].date   = tr_date();
219    tr_lockUnlock( &f->lock );
220   
221    return f->open[winner].file;
222}
223
224/***********************************************************************
225 * tr_fdFileRelease
226 **********************************************************************/
227void tr_fdFileRelease( tr_fd_t * f, int file )
228{
229    int i;
230    tr_lockLock( &f->lock );
231
232    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
233    {
234        if( f->open[i].file == file )
235        {
236            f->open[i].status = STATUS_UNUSED;
237            break;
238        }
239    }
240   
241    tr_lockUnlock( &f->lock );
242}
243
244/***********************************************************************
245 * tr_fdFileClose
246 **********************************************************************/
247void tr_fdFileClose( tr_fd_t * f, char * folder, char * name )
248{
249    int i;
250
251    tr_lockLock( &f->lock );
252
253    /* Is it already open? */
254    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
255    {
256        if( f->open[i].status & STATUS_INVALID )
257        {
258            continue;
259        }
260        if( !strcmp( folder, f->open[i].folder ) &&
261            !strcmp( name, f->open[i].name ) )
262        {
263            CloseFile( f, i );
264        }
265    }
266
267    tr_lockUnlock( &f->lock );
268}
269
270/***********************************************************************
271 * tr_fdSocketWillCreate
272 **********************************************************************/
273int tr_fdSocketWillCreate( tr_fd_t * f, int reserved )
274{
275    int ret;
276
277    tr_lockLock( &f->lock );
278
279    if( reserved )
280    {
281        if( f->reserved < TR_RESERVED_FDS )
282        {
283            ret = 0;
284            (f->reserved)++;
285        }
286        else
287        {
288            ret = 1;
289        }
290    }
291    else
292    {
293        if( f->normal < f->normalMax )
294        {
295            ret = 0;
296            (f->normal)++;
297        }
298        else
299        {
300            ret = 1;
301        }
302    }
303
304    tr_lockUnlock( &f->lock );
305
306    return ret;
307}
308
309/***********************************************************************
310 * tr_fdSocketClosed
311 **********************************************************************/
312void tr_fdSocketClosed( tr_fd_t * f, int reserved )
313{
314    tr_lockLock( &f->lock );
315
316    if( reserved )
317    {
318        (f->reserved)--;
319    }
320    else
321    {
322        (f->normal)--;
323    }
324
325    tr_lockUnlock( &f->lock );
326}
327
328/***********************************************************************
329 * tr_fdClose
330 **********************************************************************/
331void tr_fdClose( tr_fd_t * f )
332{
333    tr_lockClose( &f->lock );
334    free( f );
335}
336
337
338/***********************************************************************
339 * Local functions
340 **********************************************************************/
341
342/***********************************************************************
343 * CloseFile
344 ***********************************************************************
345 * We first mark it as closing then release the lock while doing so,
346 * because close() may take same time and we don't want to block other
347 * threads.
348 **********************************************************************/
349static void CloseFile( tr_fd_t * f, int i )
350{
351    if( !( f->open[i].status & STATUS_UNUSED ) )
352    {
353        tr_err( "CloseFile: status is %d, should be %d",
354                f->open[i].status, STATUS_UNUSED );
355    }
356    tr_dbg( "Closing %s in %s", f->open[i].name, f->open[i].folder );
357    f->open[i].status = STATUS_CLOSING;
358    tr_lockUnlock( &f->lock );
359    close( f->open[i].file );
360    tr_lockLock( &f->lock );
361    f->open[i].status = STATUS_INVALID;
362}
363
364/***********************************************************************
365 * CheckCanOpen
366 ***********************************************************************
367 *
368 **********************************************************************/
369static int CheckCanOpen( char * folder, char * name, int write )
370{
371    return 0;
372}
Note: See TracBrowser for help on using the repository browser.