1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. 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 beginning 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_HEAD(&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 last resource in the list */
210 res
= APR_RING_LAST(&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 APR_RING_REMOVE(res
, link
);
220 rv
= destroy_resource(reslist
, res
);
221 free_container(reslist
, res
);
222 if (rv
!= APR_SUCCESS
) {
223 apr_thread_mutex_unlock(reslist
->listlock
);
228 apr_thread_mutex_unlock(reslist
->listlock
);
232 APU_DECLARE(apr_status_t
) apr_reslist_create(apr_reslist_t
**reslist
,
233 int min
, int smax
, int hmax
,
234 apr_interval_time_t ttl
,
235 apr_reslist_constructor con
,
236 apr_reslist_destructor de
,
243 /* Do some sanity checks so we don't thrash around in the
244 * maintenance routine later. */
245 if (min
> smax
|| min
> hmax
|| smax
> hmax
|| ttl
< 0) {
249 rl
= apr_pcalloc(pool
, sizeof(*rl
));
255 rl
->constructor
= con
;
259 APR_RING_INIT(&rl
->avail_list
, apr_res_t
, link
);
260 APR_RING_INIT(&rl
->free_list
, apr_res_t
, link
);
262 rv
= apr_thread_mutex_create(&rl
->listlock
, APR_THREAD_MUTEX_DEFAULT
,
264 if (rv
!= APR_SUCCESS
) {
267 rv
= apr_thread_cond_create(&rl
->avail
, pool
);
268 if (rv
!= APR_SUCCESS
) {
272 rv
= reslist_maint(rl
);
273 if (rv
!= APR_SUCCESS
) {
277 apr_pool_cleanup_register(rl
->pool
, rl
, reslist_cleanup
,
278 apr_pool_cleanup_null
);
285 APU_DECLARE(apr_status_t
) apr_reslist_destroy(apr_reslist_t
*reslist
)
287 return apr_pool_cleanup_run(reslist
->pool
, reslist
, reslist_cleanup
);
290 APU_DECLARE(apr_status_t
) apr_reslist_acquire(apr_reslist_t
*reslist
,
296 apr_thread_mutex_lock(reslist
->listlock
);
297 /* If there are idle resources on the available list, use
298 * them right away. */
299 if (reslist
->nidle
> 0) {
300 /* Pop off the first resource */
301 res
= pop_resource(reslist
);
302 *resource
= res
->opaque
;
303 free_container(reslist
, res
);
304 apr_thread_mutex_unlock(reslist
->listlock
);
307 /* If we've hit our max, block until we're allowed to create
308 * a new one, or something becomes free. */
309 else while (reslist
->ntotal
>= reslist
->hmax
310 && reslist
->nidle
<= 0) {
311 if (reslist
->timeout
) {
312 if ((rv
= apr_thread_cond_timedwait(reslist
->avail
,
313 reslist
->listlock
, reslist
->timeout
)) != APR_SUCCESS
) {
314 apr_thread_mutex_unlock(reslist
->listlock
);
319 apr_thread_cond_wait(reslist
->avail
, reslist
->listlock
);
321 /* If we popped out of the loop, first try to see if there
322 * are new resources available for immediate use. */
323 if (reslist
->nidle
> 0) {
324 res
= pop_resource(reslist
);
325 *resource
= res
->opaque
;
326 free_container(reslist
, res
);
327 apr_thread_mutex_unlock(reslist
->listlock
);
330 /* Otherwise the reason we dropped out of the loop
331 * was because there is a new slot available, so create
332 * a resource to fill the slot and use it. */
334 rv
= create_resource(reslist
, &res
);
335 if (rv
== APR_SUCCESS
) {
337 *resource
= res
->opaque
;
339 free_container(reslist
, res
);
340 apr_thread_mutex_unlock(reslist
->listlock
);
345 APU_DECLARE(apr_status_t
) apr_reslist_release(apr_reslist_t
*reslist
,
350 apr_thread_mutex_lock(reslist
->listlock
);
351 res
= get_container(reslist
);
352 res
->opaque
= resource
;
353 push_resource(reslist
, res
);
354 apr_thread_cond_signal(reslist
->avail
);
355 apr_thread_mutex_unlock(reslist
->listlock
);
357 return reslist_maint(reslist
);
360 APU_DECLARE(void) apr_reslist_timeout_set(apr_reslist_t
*reslist
,
361 apr_interval_time_t timeout
)
363 reslist
->timeout
= timeout
;
366 APU_DECLARE(apr_uint32_t
) apr_reslist_acquired_count(apr_reslist_t
*reslist
)
370 apr_thread_mutex_lock(reslist
->listlock
);
371 count
= reslist
->ntotal
- reslist
->nidle
;
372 apr_thread_mutex_unlock(reslist
->listlock
);
377 APU_DECLARE(apr_status_t
) apr_reslist_invalidate(apr_reslist_t
*reslist
,
381 apr_thread_mutex_lock(reslist
->listlock
);
382 ret
= reslist
->destructor(resource
, reslist
->params
, reslist
->pool
);
384 apr_thread_mutex_unlock(reslist
->listlock
);
388 #endif /* APR_HAS_THREADS */