1
/* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
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
8
* http://www.apache.org/licenses/LICENSE-2.0
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.
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"
30
* A single resource element.
35
APR_RING_ENTRY(apr_res_t) link;
37
typedef struct apr_res_t apr_res_t;
40
* A ring of resources representing the list of available resources.
42
APR_RING_HEAD(apr_resring_t, apr_res_t);
43
typedef struct apr_resring_t apr_resring_t;
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;
64
* Grab a resource from the front of the resource list.
65
* Assumes: that the reslist is locked.
67
static apr_res_t *pop_resource(apr_reslist_t *reslist)
70
res = APR_RING_FIRST(&reslist->avail_list);
71
APR_RING_REMOVE(res, link);
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.
81
static void push_resource(apr_reslist_t *reslist, apr_res_t *resource)
83
APR_RING_INSERT_TAIL(&reslist->avail_list, resource, apr_res_t, link);
84
resource->freed = apr_time_now();
89
* Get an resource container from the free list or create a new one.
91
static apr_res_t *get_container(apr_reslist_t *reslist)
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);
100
res = apr_pcalloc(reslist->pool, sizeof(*res));
105
* Free up a resource container by placing it on the free list.
107
static void free_container(apr_reslist_t *reslist, apr_res_t *container)
109
APR_RING_INSERT_TAIL(&reslist->free_list, container, apr_res_t, link);
113
* Create a new resource and return it.
114
* Assumes: that the reslist is locked.
116
static apr_status_t create_resource(apr_reslist_t *reslist, apr_res_t **ret_res)
121
res = get_container(reslist);
123
rv = reslist->constructor(&res->opaque, reslist->params, reslist->pool);
130
* Destroy a single idle resource.
131
* Assumes: that the reslist is locked.
133
static apr_status_t destroy_resource(apr_reslist_t *reslist, apr_res_t *res)
135
return reslist->destructor(res->opaque, reslist->params, reslist->pool);
138
static apr_status_t reslist_cleanup(void *data_)
141
apr_reslist_t *rl = data_;
144
apr_thread_mutex_lock(rl->listlock);
146
while (rl->nidle > 0) {
147
res = pop_resource(rl);
149
rv = destroy_resource(rl, res);
150
if (rv != APR_SUCCESS) {
153
free_container(rl, res);
156
assert(rl->nidle == 0);
157
assert(rl->ntotal == 0);
159
apr_thread_mutex_destroy(rl->listlock);
160
apr_thread_cond_destroy(rl->avail);
166
* Perform routine maintenance on the resource list. This call
167
* may instantiate new resources or expire old resources.
169
static apr_status_t reslist_maint(apr_reslist_t *reslist)
176
apr_thread_mutex_lock(reslist->listlock);
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);
187
/* Add it to the list */
188
push_resource(reslist, res);
189
/* Update our counters */
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);
200
/* We don't need to see if we're over the max if we were under it before */
202
apr_thread_mutex_unlock(reslist->listlock);
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. */
217
res = pop_resource(reslist);
219
rv = destroy_resource(reslist, res);
220
free_container(reslist, res);
221
if (rv != APR_SUCCESS) {
222
apr_thread_mutex_unlock(reslist->listlock);
227
apr_thread_mutex_unlock(reslist->listlock);
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,
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) {
248
rl = apr_pcalloc(pool, sizeof(*rl));
254
rl->constructor = con;
258
APR_RING_INIT(&rl->avail_list, apr_res_t, link);
259
APR_RING_INIT(&rl->free_list, apr_res_t, link);
261
rv = apr_thread_mutex_create(&rl->listlock, APR_THREAD_MUTEX_DEFAULT,
263
if (rv != APR_SUCCESS) {
266
rv = apr_thread_cond_create(&rl->avail, pool);
267
if (rv != APR_SUCCESS) {
271
rv = reslist_maint(rl);
272
if (rv != APR_SUCCESS) {
276
apr_pool_cleanup_register(rl->pool, rl, reslist_cleanup,
277
apr_pool_cleanup_null);
284
APU_DECLARE(apr_status_t) apr_reslist_destroy(apr_reslist_t *reslist)
286
return apr_pool_cleanup_run(reslist->pool, reslist, reslist_cleanup);
289
APU_DECLARE(apr_status_t) apr_reslist_acquire(apr_reslist_t *reslist,
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);
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);
318
apr_thread_cond_wait(reslist->avail, reslist->listlock);
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);
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. */
333
rv = create_resource(reslist, &res);
334
if (rv == APR_SUCCESS) {
336
*resource = res->opaque;
338
free_container(reslist, res);
339
apr_thread_mutex_unlock(reslist->listlock);
344
APU_DECLARE(apr_status_t) apr_reslist_release(apr_reslist_t *reslist,
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);
356
return reslist_maint(reslist);
359
APU_DECLARE(void) apr_reslist_timeout_set(apr_reslist_t *reslist,
360
apr_interval_time_t timeout)
362
reslist->timeout = timeout;
365
APU_DECLARE(apr_status_t) apr_reslist_invalidate(apr_reslist_t *reslist,
369
apr_thread_mutex_lock(reslist->listlock);
370
ret = reslist->destructor(resource, reslist->params, reslist->pool);
372
apr_thread_mutex_unlock(reslist->listlock);
376
#endif /* APR_HAS_THREADS */