~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to srclib/apr-util/misc/apr_reslist.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
 
2
 * applicable.
 
3
 *
 
4
 * Licensed under the Apache License, Version 2.0 (the "License");
 
5
 * you may not use this file except in compliance with the License.
 
6
 * You may obtain a copy of the License at
 
7
 *
 
8
 *     http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 * Unless required by applicable law or agreed to in writing, software
 
11
 * distributed under the License is distributed on an "AS IS" BASIS,
 
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
 * See the License for the specific language governing permissions and
 
14
 * limitations under the License.
 
15
 */
 
16
 
 
17
#include <assert.h>
 
18
 
 
19
#include "apu.h"
 
20
#include "apr_reslist.h"
 
21
#include "apr_errno.h"
 
22
#include "apr_strings.h"
 
23
#include "apr_thread_mutex.h"
 
24
#include "apr_thread_cond.h"
 
25
#include "apr_ring.h"
 
26
 
 
27
#if APR_HAS_THREADS
 
28
 
 
29
/**
 
30
 * A single resource element.
 
31
 */
 
32
struct apr_res_t {
 
33
    apr_time_t freed;
 
34
    void *opaque;
 
35
    APR_RING_ENTRY(apr_res_t) link;
 
36
};
 
37
typedef struct apr_res_t apr_res_t;
 
38
 
 
39
/**
 
40
 * A ring of resources representing the list of available resources.
 
41
 */
 
42
APR_RING_HEAD(apr_resring_t, apr_res_t);
 
43
typedef struct apr_resring_t apr_resring_t;
 
44
 
 
45
struct apr_reslist_t {
 
46
    apr_pool_t *pool; /* the pool used in constructor and destructor calls */
 
47
    int ntotal;     /* total number of resources managed by this list */
 
48
    int nidle;      /* number of available resources */
 
49
    int min;  /* desired minimum number of available resources */
 
50
    int smax; /* soft maximum on the total number of resources */
 
51
    int hmax; /* hard maximum on the total number of resources */
 
52
    apr_interval_time_t ttl; /* TTL when we have too many resources */
 
53
    apr_interval_time_t timeout; /* Timeout for waiting on resource */
 
54
    apr_reslist_constructor constructor;
 
55
    apr_reslist_destructor destructor;
 
56
    void *params; /* opaque data passed to constructor and destructor calls */
 
57
    apr_resring_t avail_list;
 
58
    apr_resring_t free_list;
 
59
    apr_thread_mutex_t *listlock;
 
60
    apr_thread_cond_t *avail;
 
61
};
 
62
 
 
63
/**
 
64
 * Grab a resource from the front of the resource list.
 
65
 * Assumes: that the reslist is locked.
 
66
 */
 
67
static apr_res_t *pop_resource(apr_reslist_t *reslist)
 
68
{
 
69
    apr_res_t *res;
 
70
    res = APR_RING_FIRST(&reslist->avail_list);
 
71
    APR_RING_REMOVE(res, link);
 
72
    reslist->nidle--;
 
73
    return res;
 
74
}
 
75
 
 
76
/**
 
77
 * Add a resource to the end of the list, set the time at which
 
78
 * it was added to the list.
 
79
 * Assumes: that the reslist is locked.
 
80
 */
 
81
static void push_resource(apr_reslist_t *reslist, apr_res_t *resource)
 
82
{
 
83
    APR_RING_INSERT_TAIL(&reslist->avail_list, resource, apr_res_t, link);
 
84
    resource->freed = apr_time_now();
 
85
    reslist->nidle++;
 
86
}
 
87
 
 
88
/**
 
89
 * Get an resource container from the free list or create a new one.
 
90
 */
 
91
static apr_res_t *get_container(apr_reslist_t *reslist)
 
92
{
 
93
    apr_res_t *res;
 
94
 
 
95
    if (!APR_RING_EMPTY(&reslist->free_list, apr_res_t, link)) {
 
96
        res = APR_RING_FIRST(&reslist->free_list);
 
97
        APR_RING_REMOVE(res, link);
 
98
    }
 
99
    else
 
100
        res = apr_pcalloc(reslist->pool, sizeof(*res));
 
101
    return res;
 
102
}
 
103
 
 
104
/**
 
105
 * Free up a resource container by placing it on the free list.
 
106
 */
 
107
static void free_container(apr_reslist_t *reslist, apr_res_t *container)
 
108
{
 
109
    APR_RING_INSERT_TAIL(&reslist->free_list, container, apr_res_t, link);
 
110
}
 
111
 
 
112
/**
 
113
 * Create a new resource and return it.
 
114
 * Assumes: that the reslist is locked.
 
115
 */
 
116
static apr_status_t create_resource(apr_reslist_t *reslist, apr_res_t **ret_res)
 
117
{
 
118
    apr_status_t rv;
 
119
    apr_res_t *res;
 
120
 
 
121
    res = get_container(reslist);
 
122
 
 
123
    rv = reslist->constructor(&res->opaque, reslist->params, reslist->pool);
 
124
 
 
125
    *ret_res = res;
 
126
    return rv;
 
127
}
 
128
 
 
129
/**
 
130
 * Destroy a single idle resource.
 
131
 * Assumes: that the reslist is locked.
 
132
 */
 
133
static apr_status_t destroy_resource(apr_reslist_t *reslist, apr_res_t *res)
 
134
{
 
135
    return reslist->destructor(res->opaque, reslist->params, reslist->pool);
 
136
}
 
137
 
 
138
static apr_status_t reslist_cleanup(void *data_)
 
139
{
 
140
    apr_status_t rv;
 
141
    apr_reslist_t *rl = data_;
 
142
    apr_res_t *res;
 
143
 
 
144
    apr_thread_mutex_lock(rl->listlock);
 
145
 
 
146
    while (rl->nidle > 0) {
 
147
        res = pop_resource(rl);
 
148
        rl->ntotal--;
 
149
        rv = destroy_resource(rl, res);
 
150
        if (rv != APR_SUCCESS) {
 
151
            return rv;
 
152
        }
 
153
        free_container(rl, res);
 
154
    }
 
155
 
 
156
    assert(rl->nidle == 0);
 
157
    assert(rl->ntotal == 0);
 
158
 
 
159
    apr_thread_mutex_destroy(rl->listlock);
 
160
    apr_thread_cond_destroy(rl->avail);
 
161
 
 
162
    return APR_SUCCESS;
 
163
}
 
164
 
 
165
/**
 
166
 * Perform routine maintenance on the resource list. This call
 
167
 * may instantiate new resources or expire old resources.
 
168
 */
 
169
static apr_status_t reslist_maint(apr_reslist_t *reslist)
 
170
{
 
171
    apr_time_t now;
 
172
    apr_status_t rv;
 
173
    apr_res_t *res;
 
174
    int created_one = 0;
 
175
 
 
176
    apr_thread_mutex_lock(reslist->listlock);
 
177
 
 
178
    /* Check if we need to create more resources, and if we are allowed to. */
 
179
    while (reslist->nidle < reslist->min && reslist->ntotal <= reslist->hmax) {
 
180
        /* Create the resource */
 
181
        rv = create_resource(reslist, &res);
 
182
        if (rv != APR_SUCCESS) {
 
183
            free_container(reslist, res);
 
184
            apr_thread_mutex_unlock(reslist->listlock);
 
185
            return rv;
 
186
        }
 
187
        /* Add it to the list */
 
188
        push_resource(reslist, res);
 
189
        /* Update our counters */
 
190
        reslist->ntotal++;
 
191
        /* If someone is waiting on that guy, wake them up. */
 
192
        rv = apr_thread_cond_signal(reslist->avail);
 
193
        if (rv != APR_SUCCESS) {
 
194
            apr_thread_mutex_unlock(reslist->listlock);
 
195
            return rv;
 
196
        }
 
197
        created_one++;
 
198
    }
 
199
 
 
200
    /* We don't need to see if we're over the max if we were under it before */
 
201
    if (created_one) {
 
202
        apr_thread_mutex_unlock(reslist->listlock);
 
203
        return APR_SUCCESS;
 
204
    }
 
205
 
 
206
    /* Check if we need to expire old resources */
 
207
    now = apr_time_now();
 
208
    while (reslist->nidle > reslist->smax && reslist->nidle > 0) {
 
209
        /* Peak at the first resource in the list */
 
210
        res = APR_RING_FIRST(&reslist->avail_list);
 
211
        /* See if the oldest entry should be expired */
 
212
        if (now - res->freed < reslist->ttl) {
 
213
            /* If this entry is too young, none of the others
 
214
             * will be ready to be expired either, so we are done. */
 
215
            break;
 
216
        }
 
217
        res = pop_resource(reslist);
 
218
        reslist->ntotal--;
 
219
        rv = destroy_resource(reslist, res);
 
220
        free_container(reslist, res);
 
221
        if (rv != APR_SUCCESS) {
 
222
            apr_thread_mutex_unlock(reslist->listlock);
 
223
            return rv;
 
224
        }
 
225
    }
 
226
 
 
227
    apr_thread_mutex_unlock(reslist->listlock);
 
228
    return APR_SUCCESS;
 
229
}
 
230
 
 
231
APU_DECLARE(apr_status_t) apr_reslist_create(apr_reslist_t **reslist,
 
232
                                             int min, int smax, int hmax,
 
233
                                             apr_interval_time_t ttl,
 
234
                                             apr_reslist_constructor con,
 
235
                                             apr_reslist_destructor de,
 
236
                                             void *params,
 
237
                                             apr_pool_t *pool)
 
238
{
 
239
    apr_status_t rv;
 
240
    apr_reslist_t *rl;
 
241
 
 
242
    /* Do some sanity checks so we don't thrash around in the
 
243
     * maintenance routine later. */
 
244
    if (min > smax || min > hmax || smax > hmax || ttl < 0) {
 
245
        return APR_EINVAL;
 
246
    }
 
247
 
 
248
    rl = apr_pcalloc(pool, sizeof(*rl));
 
249
    rl->pool = pool;
 
250
    rl->min = min;
 
251
    rl->smax = smax;
 
252
    rl->hmax = hmax;
 
253
    rl->ttl = ttl;
 
254
    rl->constructor = con;
 
255
    rl->destructor = de;
 
256
    rl->params = params;
 
257
 
 
258
    APR_RING_INIT(&rl->avail_list, apr_res_t, link);
 
259
    APR_RING_INIT(&rl->free_list, apr_res_t, link);
 
260
 
 
261
    rv = apr_thread_mutex_create(&rl->listlock, APR_THREAD_MUTEX_DEFAULT,
 
262
                                 pool);
 
263
    if (rv != APR_SUCCESS) {
 
264
        return rv;
 
265
    }
 
266
    rv = apr_thread_cond_create(&rl->avail, pool);
 
267
    if (rv != APR_SUCCESS) {
 
268
        return rv;
 
269
    }
 
270
 
 
271
    rv = reslist_maint(rl);
 
272
    if (rv != APR_SUCCESS) {
 
273
        return rv;
 
274
    }
 
275
 
 
276
    apr_pool_cleanup_register(rl->pool, rl, reslist_cleanup,
 
277
                              apr_pool_cleanup_null);
 
278
 
 
279
    *reslist = rl;
 
280
 
 
281
    return APR_SUCCESS;
 
282
}
 
283
 
 
284
APU_DECLARE(apr_status_t) apr_reslist_destroy(apr_reslist_t *reslist)
 
285
{
 
286
    return apr_pool_cleanup_run(reslist->pool, reslist, reslist_cleanup);
 
287
}
 
288
 
 
289
APU_DECLARE(apr_status_t) apr_reslist_acquire(apr_reslist_t *reslist,
 
290
                                              void **resource)
 
291
{
 
292
    apr_status_t rv;
 
293
    apr_res_t *res;
 
294
 
 
295
    apr_thread_mutex_lock(reslist->listlock);
 
296
    /* If there are idle resources on the available list, use
 
297
     * them right away. */
 
298
    if (reslist->nidle > 0) {
 
299
        /* Pop off the first resource */
 
300
        res = pop_resource(reslist);
 
301
        *resource = res->opaque;
 
302
        free_container(reslist, res);
 
303
        apr_thread_mutex_unlock(reslist->listlock);
 
304
        return APR_SUCCESS;
 
305
    }
 
306
    /* If we've hit our max, block until we're allowed to create
 
307
     * a new one, or something becomes free. */
 
308
    else while (reslist->ntotal >= reslist->hmax
 
309
                && reslist->nidle <= 0) {
 
310
        if (reslist->timeout) {
 
311
            if ((rv = apr_thread_cond_timedwait(reslist->avail, 
 
312
                reslist->listlock, reslist->timeout)) != APR_SUCCESS) {
 
313
                apr_thread_mutex_unlock(reslist->listlock);
 
314
                return rv;
 
315
            }
 
316
        }
 
317
        else
 
318
            apr_thread_cond_wait(reslist->avail, reslist->listlock);
 
319
    }
 
320
    /* If we popped out of the loop, first try to see if there
 
321
     * are new resources available for immediate use. */
 
322
    if (reslist->nidle > 0) {
 
323
        res = pop_resource(reslist);
 
324
        *resource = res->opaque;
 
325
        free_container(reslist, res);
 
326
        apr_thread_mutex_unlock(reslist->listlock);
 
327
        return APR_SUCCESS;
 
328
    }
 
329
    /* Otherwise the reason we dropped out of the loop
 
330
     * was because there is a new slot available, so create
 
331
     * a resource to fill the slot and use it. */
 
332
    else {
 
333
        rv = create_resource(reslist, &res);
 
334
        if (rv == APR_SUCCESS) {
 
335
            reslist->ntotal++;
 
336
            *resource = res->opaque;
 
337
        }
 
338
        free_container(reslist, res);
 
339
        apr_thread_mutex_unlock(reslist->listlock);
 
340
        return rv;
 
341
    }
 
342
}
 
343
 
 
344
APU_DECLARE(apr_status_t) apr_reslist_release(apr_reslist_t *reslist,
 
345
                                              void *resource)
 
346
{
 
347
    apr_res_t *res;
 
348
 
 
349
    apr_thread_mutex_lock(reslist->listlock);
 
350
    res = get_container(reslist);
 
351
    res->opaque = resource;
 
352
    push_resource(reslist, res);
 
353
    apr_thread_cond_signal(reslist->avail);
 
354
    apr_thread_mutex_unlock(reslist->listlock);
 
355
 
 
356
    return reslist_maint(reslist);
 
357
}
 
358
 
 
359
APU_DECLARE(void) apr_reslist_timeout_set(apr_reslist_t *reslist,
 
360
                                          apr_interval_time_t timeout)
 
361
{
 
362
    reslist->timeout = timeout;
 
363
}
 
364
 
 
365
APU_DECLARE(apr_status_t) apr_reslist_invalidate(apr_reslist_t *reslist,
 
366
                                                 void *resource)
 
367
{
 
368
    apr_status_t ret;
 
369
    apr_thread_mutex_lock(reslist->listlock);
 
370
    ret = reslist->destructor(resource, reslist->params, reslist->pool);
 
371
    reslist->ntotal--;
 
372
    apr_thread_mutex_unlock(reslist->listlock);
 
373
    return ret;
 
374
}
 
375
 
 
376
#endif  /* APR_HAS_THREADS */