source: trunk/libtransmission/fdlimit.c @ 238

Last change on this file since 238 was 238, checked in by titer, 16 years ago

Use file descriptors instead of streams (preliminary cleanup for fixes
to come in fdlimit*)

File size: 7.2 KB
Line 
1/******************************************************************************
2 * Copyright (c) 2005 Eric Petit
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *****************************************************************************/
22
23#include "transmission.h"
24
25#define TR_MAX_OPEN_FILES 16 /* That is, real files, not sockets */
26#define TR_RESERVED_FDS   16 /* Number of sockets reserved for
27                                connections to trackers */
28
29typedef struct tr_openFile_s
30{
31    char       path[MAX_PATH_LENGTH];
32    int        file;
33
34#define STATUS_INVALID 1
35#define STATUS_UNUSED  2
36#define STATUS_USED    4
37#define STATUS_CLOSING 8
38    int        status;
39
40    uint64_t   date;
41
42} tr_openFile_t;
43
44struct tr_fd_s
45{
46    tr_lock_t       lock;
47   
48    int             reserved;
49
50    int             normal;
51    int             normalMax;
52
53    tr_openFile_t   open[TR_MAX_OPEN_FILES];
54};
55
56/***********************************************************************
57 * tr_fdInit
58 **********************************************************************/
59tr_fd_t * tr_fdInit()
60{
61    tr_fd_t * f;
62    int i, j, s[4096];
63
64    f = calloc( sizeof( tr_fd_t ), 1 );
65
66    /* Init lock */
67    tr_lockInit( &f->lock );
68
69    /* Detect the maximum number of open files or sockets */
70    for( i = 0; i < 4096; i++ )
71    {
72        if( ( s[i] = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
73        {
74            break;
75        }
76    }
77    for( j = 0; j < i; j++ )
78    {
79        tr_netClose( s[j] );
80    }
81
82    tr_dbg( "%d usable file descriptors", i );
83
84    f->reserved  = 0;
85    f->normal    = 0;
86
87    f->normalMax = i - TR_RESERVED_FDS - 10;
88        /* To be safe, in case the UI needs to write a preferences file
89           or something */
90
91    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
92    {
93        f->open[i].status = STATUS_INVALID;
94    }
95
96    return f;
97}
98
99/***********************************************************************
100 * tr_fdFileOpen
101 **********************************************************************/
102int tr_fdFileOpen( tr_fd_t * f, char * path )
103{
104    int i, winner;
105    uint64_t date;
106
107    tr_lockLock( &f->lock );
108
109    /* Is it already open? */
110    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
111    {
112        if( f->open[i].status > STATUS_INVALID &&
113            !strcmp( path, f->open[i].path ) )
114        {
115            if( f->open[i].status & STATUS_CLOSING )
116            {
117                /* Wait until the file is closed */
118                tr_lockUnlock( &f->lock );
119                tr_wait( 10 );
120                tr_lockLock( &f->lock );
121                i = -1;
122                continue;
123            }
124            winner = i;
125            goto done;
126        }
127    }
128
129    /* Can we open one more file? */
130    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
131    {
132        if( f->open[i].status & STATUS_INVALID )
133        {
134            winner = i;
135            goto open;
136        }
137    }
138
139    for( ;; )
140    {
141        /* Close the oldest currently unused file */
142        date   = tr_date() + 1;
143        winner = -1;
144
145        for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
146        {
147            if( !( f->open[i].status & STATUS_UNUSED ) )
148            {
149                continue;
150            }
151            if( f->open[i].date < date )
152            {
153                winner = i;
154                date   = f->open[i].date;
155            }
156        }
157
158        if( winner >= 0 )
159        {
160            /* Close the file: we mark it as closing then release the
161               lock while doing so, because close may take same time
162               and we don't want to block other threads */
163            tr_dbg( "Closing %s", f->open[winner].path );
164            f->open[winner].status = STATUS_CLOSING;
165            tr_lockUnlock( &f->lock );
166            close( f->open[winner].file );
167            tr_lockLock( &f->lock );
168            goto open;
169        }
170
171        /* All used! Wait a bit and try again */
172        tr_lockUnlock( &f->lock );
173        tr_wait( 10 );
174        tr_lockLock( &f->lock );
175    }
176
177open:
178    tr_dbg( "Opening %s", path );
179    snprintf( f->open[winner].path, MAX_PATH_LENGTH, "%s", path );
180    f->open[winner].file = open( path, O_RDWR, 0 );
181
182done:
183    f->open[winner].status = STATUS_USED;
184    f->open[winner].date   = tr_date();
185    tr_lockUnlock( &f->lock );
186   
187    return f->open[winner].file;
188}
189
190/***********************************************************************
191 * tr_fdFileRelease
192 **********************************************************************/
193void tr_fdFileRelease( tr_fd_t * f, int file )
194{
195    int i;
196    tr_lockLock( &f->lock );
197
198    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
199    {
200        if( f->open[i].file == file )
201        {
202            f->open[i].status = STATUS_UNUSED;
203            break;
204        }
205    }
206   
207    tr_lockUnlock( &f->lock );
208}
209
210/***********************************************************************
211 * tr_fdFileClose
212 **********************************************************************/
213void tr_fdFileClose( tr_fd_t * f, char * path )
214{
215    int i;
216
217    tr_lockLock( &f->lock );
218
219    /* Is it already open? */
220    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
221    {
222        if( f->open[i].status & STATUS_INVALID )
223        {
224            continue;
225        }
226        if( !strcmp( path, f->open[i].path ) )
227        {
228            tr_dbg( "Closing %s", path );
229            close( f->open[i].file );
230            f->open[i].status = STATUS_INVALID;
231            break;
232        }
233    }
234
235    tr_lockUnlock( &f->lock );
236}
237
238int tr_fdSocketWillCreate( tr_fd_t * f, int reserved )
239{
240    int ret;
241
242    tr_lockLock( &f->lock );
243
244    if( reserved )
245    {
246        if( f->reserved < TR_RESERVED_FDS )
247        {
248            ret = 0;
249            (f->reserved)++;
250        }
251        else
252        {
253            ret = 1;
254        }
255    }
256    else
257    {
258        if( f->normal < f->normalMax )
259        {
260            ret = 0;
261            (f->normal)++;
262        }
263        else
264        {
265            ret = 1;
266        }
267    }
268
269    tr_lockUnlock( &f->lock );
270
271    return ret;
272}
273
274void tr_fdSocketClosed( tr_fd_t * f, int reserved )
275{
276    tr_lockLock( &f->lock );
277
278    if( reserved )
279    {
280        (f->reserved)--;
281    }
282    else
283    {
284        (f->normal)--;
285    }
286
287    tr_lockUnlock( &f->lock );
288}
289
290void tr_fdClose( tr_fd_t * f )
291{
292    tr_lockClose( &f->lock );
293    free( f );
294}
295
Note: See TracBrowser for help on using the repository browser.