1251876Speter/* Licensed to the Apache Software Foundation (ASF) under one or more 2251876Speter * contributor license agreements. See the NOTICE file distributed with 3251876Speter * this work for additional information regarding copyright ownership. 4251876Speter * The ASF licenses this file to You under the Apache License, Version 2.0 5251876Speter * (the "License"); you may not use this file except in compliance with 6251876Speter * the License. You may obtain a copy of the License at 7251876Speter * 8251876Speter * http://www.apache.org/licenses/LICENSE-2.0 9251876Speter * 10251876Speter * Unless required by applicable law or agreed to in writing, software 11251876Speter * distributed under the License is distributed on an "AS IS" BASIS, 12251876Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13251876Speter * See the License for the specific language governing permissions and 14251876Speter * limitations under the License. 15251876Speter */ 16251876Speter 17251876Speter#include <assert.h> 18251876Speter 19251876Speter#include "apu.h" 20251876Speter#include "apr_reslist.h" 21251876Speter#include "apr_errno.h" 22251876Speter#include "apr_strings.h" 23251876Speter#include "apr_thread_mutex.h" 24251876Speter#include "apr_thread_cond.h" 25251876Speter#include "apr_ring.h" 26251876Speter 27251876Speter/** 28251876Speter * A single resource element. 29251876Speter */ 30251876Speterstruct apr_res_t { 31251876Speter apr_time_t freed; 32251876Speter void *opaque; 33251876Speter APR_RING_ENTRY(apr_res_t) link; 34251876Speter}; 35251876Spetertypedef struct apr_res_t apr_res_t; 36251876Speter 37251876Speter/** 38251876Speter * A ring of resources representing the list of available resources. 39251876Speter */ 40251876SpeterAPR_RING_HEAD(apr_resring_t, apr_res_t); 41251876Spetertypedef struct apr_resring_t apr_resring_t; 42251876Speter 43251876Speterstruct apr_reslist_t { 44251876Speter apr_pool_t *pool; /* the pool used in constructor and destructor calls */ 45251876Speter int ntotal; /* total number of resources managed by this list */ 46251876Speter int nidle; /* number of available resources */ 47251876Speter int min; /* desired minimum number of available resources */ 48251876Speter int smax; /* soft maximum on the total number of resources */ 49251876Speter int hmax; /* hard maximum on the total number of resources */ 50251876Speter apr_interval_time_t ttl; /* TTL when we have too many resources */ 51251876Speter apr_interval_time_t timeout; /* Timeout for waiting on resource */ 52251876Speter apr_reslist_constructor constructor; 53251876Speter apr_reslist_destructor destructor; 54251876Speter void *params; /* opaque data passed to constructor and destructor calls */ 55251876Speter apr_resring_t avail_list; 56251876Speter apr_resring_t free_list; 57251876Speter#if APR_HAS_THREADS 58251876Speter apr_thread_mutex_t *listlock; 59251876Speter apr_thread_cond_t *avail; 60251876Speter#endif 61251876Speter}; 62251876Speter 63251876Speter/** 64251876Speter * Grab a resource from the front of the resource list. 65251876Speter * Assumes: that the reslist is locked. 66251876Speter */ 67251876Speterstatic apr_res_t *pop_resource(apr_reslist_t *reslist) 68251876Speter{ 69251876Speter apr_res_t *res; 70251876Speter res = APR_RING_FIRST(&reslist->avail_list); 71251876Speter APR_RING_REMOVE(res, link); 72251876Speter reslist->nidle--; 73251876Speter return res; 74251876Speter} 75251876Speter 76251876Speter/** 77251876Speter * Add a resource to the beginning of the list, set the time at which 78251876Speter * it was added to the list. 79251876Speter * Assumes: that the reslist is locked. 80251876Speter */ 81251876Speterstatic void push_resource(apr_reslist_t *reslist, apr_res_t *resource) 82251876Speter{ 83251876Speter APR_RING_INSERT_HEAD(&reslist->avail_list, resource, apr_res_t, link); 84251876Speter resource->freed = apr_time_now(); 85251876Speter reslist->nidle++; 86251876Speter} 87251876Speter 88251876Speter/** 89251876Speter * Get an resource container from the free list or create a new one. 90251876Speter */ 91251876Speterstatic apr_res_t *get_container(apr_reslist_t *reslist) 92251876Speter{ 93251876Speter apr_res_t *res; 94251876Speter 95251876Speter if (!APR_RING_EMPTY(&reslist->free_list, apr_res_t, link)) { 96251876Speter res = APR_RING_FIRST(&reslist->free_list); 97251876Speter APR_RING_REMOVE(res, link); 98251876Speter } 99251876Speter else 100251876Speter res = apr_pcalloc(reslist->pool, sizeof(*res)); 101251876Speter return res; 102251876Speter} 103251876Speter 104251876Speter/** 105251876Speter * Free up a resource container by placing it on the free list. 106251876Speter */ 107251876Speterstatic void free_container(apr_reslist_t *reslist, apr_res_t *container) 108251876Speter{ 109251876Speter APR_RING_INSERT_TAIL(&reslist->free_list, container, apr_res_t, link); 110251876Speter} 111251876Speter 112251876Speter/** 113251876Speter * Create a new resource and return it. 114251876Speter * Assumes: that the reslist is locked. 115251876Speter */ 116251876Speterstatic apr_status_t create_resource(apr_reslist_t *reslist, apr_res_t **ret_res) 117251876Speter{ 118251876Speter apr_status_t rv; 119251876Speter apr_res_t *res; 120251876Speter 121251876Speter res = get_container(reslist); 122251876Speter 123251876Speter rv = reslist->constructor(&res->opaque, reslist->params, reslist->pool); 124251876Speter 125251876Speter *ret_res = res; 126251876Speter return rv; 127251876Speter} 128251876Speter 129251876Speter/** 130251876Speter * Destroy a single idle resource. 131251876Speter * Assumes: that the reslist is locked. 132251876Speter */ 133251876Speterstatic apr_status_t destroy_resource(apr_reslist_t *reslist, apr_res_t *res) 134251876Speter{ 135251876Speter return reslist->destructor(res->opaque, reslist->params, reslist->pool); 136251876Speter} 137251876Speter 138251876Speterstatic apr_status_t reslist_cleanup(void *data_) 139251876Speter{ 140251876Speter apr_status_t rv = APR_SUCCESS; 141251876Speter apr_reslist_t *rl = data_; 142251876Speter apr_res_t *res; 143251876Speter 144251876Speter#if APR_HAS_THREADS 145251876Speter apr_thread_mutex_lock(rl->listlock); 146251876Speter#endif 147251876Speter 148251876Speter while (rl->nidle > 0) { 149251876Speter apr_status_t rv1; 150251876Speter res = pop_resource(rl); 151251876Speter rl->ntotal--; 152251876Speter rv1 = destroy_resource(rl, res); 153251876Speter if (rv1 != APR_SUCCESS) { 154251876Speter rv = rv1; /* loses info in the unlikely event of 155251876Speter * multiple *different* failures */ 156251876Speter } 157251876Speter free_container(rl, res); 158251876Speter } 159251876Speter 160251876Speter assert(rl->nidle == 0); 161251876Speter assert(rl->ntotal == 0); 162251876Speter 163251876Speter#if APR_HAS_THREADS 164251876Speter apr_thread_mutex_unlock(rl->listlock); 165251876Speter apr_thread_mutex_destroy(rl->listlock); 166251876Speter apr_thread_cond_destroy(rl->avail); 167251876Speter#endif 168251876Speter 169251876Speter return rv; 170251876Speter} 171251876Speter 172251876Speter/** 173251876Speter * Perform routine maintenance on the resource list. This call 174251876Speter * may instantiate new resources or expire old resources. 175251876Speter */ 176251876SpeterAPU_DECLARE(apr_status_t) apr_reslist_maintain(apr_reslist_t *reslist) 177251876Speter{ 178251876Speter apr_time_t now; 179251876Speter apr_status_t rv; 180251876Speter apr_res_t *res; 181251876Speter int created_one = 0; 182251876Speter 183251876Speter#if APR_HAS_THREADS 184251876Speter apr_thread_mutex_lock(reslist->listlock); 185251876Speter#endif 186251876Speter 187251876Speter /* Check if we need to create more resources, and if we are allowed to. */ 188251876Speter while (reslist->nidle < reslist->min && reslist->ntotal < reslist->hmax) { 189251876Speter /* Create the resource */ 190251876Speter rv = create_resource(reslist, &res); 191251876Speter if (rv != APR_SUCCESS) { 192251876Speter free_container(reslist, res); 193251876Speter#if APR_HAS_THREADS 194251876Speter apr_thread_mutex_unlock(reslist->listlock); 195251876Speter#endif 196251876Speter return rv; 197251876Speter } 198251876Speter /* Add it to the list */ 199251876Speter push_resource(reslist, res); 200251876Speter /* Update our counters */ 201251876Speter reslist->ntotal++; 202251876Speter /* If someone is waiting on that guy, wake them up. */ 203251876Speter#if APR_HAS_THREADS 204251876Speter rv = apr_thread_cond_signal(reslist->avail); 205251876Speter if (rv != APR_SUCCESS) { 206251876Speter apr_thread_mutex_unlock(reslist->listlock); 207251876Speter return rv; 208251876Speter } 209251876Speter#endif 210251876Speter created_one++; 211251876Speter } 212251876Speter 213251876Speter /* We don't need to see if we're over the max if we were under it before */ 214251876Speter if (created_one) { 215251876Speter#if APR_HAS_THREADS 216251876Speter apr_thread_mutex_unlock(reslist->listlock); 217251876Speter#endif 218251876Speter return APR_SUCCESS; 219251876Speter } 220251876Speter 221251876Speter /* Check if we need to expire old resources */ 222251876Speter now = apr_time_now(); 223251876Speter while (reslist->nidle > reslist->smax && reslist->nidle > 0) { 224251876Speter /* Peak at the last resource in the list */ 225251876Speter res = APR_RING_LAST(&reslist->avail_list); 226251876Speter /* See if the oldest entry should be expired */ 227251876Speter if (now - res->freed < reslist->ttl) { 228251876Speter /* If this entry is too young, none of the others 229251876Speter * will be ready to be expired either, so we are done. */ 230251876Speter break; 231251876Speter } 232251876Speter APR_RING_REMOVE(res, link); 233251876Speter reslist->nidle--; 234251876Speter reslist->ntotal--; 235251876Speter rv = destroy_resource(reslist, res); 236251876Speter free_container(reslist, res); 237251876Speter if (rv != APR_SUCCESS) { 238251876Speter#if APR_HAS_THREADS 239251876Speter apr_thread_mutex_unlock(reslist->listlock); 240251876Speter#endif 241251876Speter return rv; 242251876Speter } 243251876Speter } 244251876Speter 245251876Speter#if APR_HAS_THREADS 246251876Speter apr_thread_mutex_unlock(reslist->listlock); 247251876Speter#endif 248251876Speter return APR_SUCCESS; 249251876Speter} 250251876Speter 251251876SpeterAPU_DECLARE(apr_status_t) apr_reslist_create(apr_reslist_t **reslist, 252251876Speter int min, int smax, int hmax, 253251876Speter apr_interval_time_t ttl, 254251876Speter apr_reslist_constructor con, 255251876Speter apr_reslist_destructor de, 256251876Speter void *params, 257251876Speter apr_pool_t *pool) 258251876Speter{ 259251876Speter apr_status_t rv; 260251876Speter apr_reslist_t *rl; 261251876Speter 262251876Speter /* Do some sanity checks so we don't thrash around in the 263251876Speter * maintenance routine later. */ 264251876Speter if (min < 0 || min > smax || min > hmax || smax > hmax || hmax == 0 || 265251876Speter ttl < 0) { 266251876Speter return APR_EINVAL; 267251876Speter } 268251876Speter 269251876Speter#if !APR_HAS_THREADS 270251876Speter /* There can be only one resource when we have no threads. */ 271251876Speter if (min > 0) { 272251876Speter min = 1; 273251876Speter } 274251876Speter if (smax > 0) { 275251876Speter smax = 1; 276251876Speter } 277251876Speter hmax = 1; 278251876Speter#endif 279251876Speter 280251876Speter rl = apr_pcalloc(pool, sizeof(*rl)); 281251876Speter rl->pool = pool; 282251876Speter rl->min = min; 283251876Speter rl->smax = smax; 284251876Speter rl->hmax = hmax; 285251876Speter rl->ttl = ttl; 286251876Speter rl->constructor = con; 287251876Speter rl->destructor = de; 288251876Speter rl->params = params; 289251876Speter 290251876Speter APR_RING_INIT(&rl->avail_list, apr_res_t, link); 291251876Speter APR_RING_INIT(&rl->free_list, apr_res_t, link); 292251876Speter 293251876Speter#if APR_HAS_THREADS 294251876Speter rv = apr_thread_mutex_create(&rl->listlock, APR_THREAD_MUTEX_DEFAULT, 295251876Speter pool); 296251876Speter if (rv != APR_SUCCESS) { 297251876Speter return rv; 298251876Speter } 299251876Speter rv = apr_thread_cond_create(&rl->avail, pool); 300251876Speter if (rv != APR_SUCCESS) { 301251876Speter return rv; 302251876Speter } 303251876Speter#endif 304251876Speter 305251876Speter rv = apr_reslist_maintain(rl); 306251876Speter if (rv != APR_SUCCESS) { 307251876Speter /* Destroy what we've created so far. 308251876Speter */ 309251876Speter reslist_cleanup(rl); 310251876Speter return rv; 311251876Speter } 312251876Speter 313251876Speter apr_pool_cleanup_register(rl->pool, rl, reslist_cleanup, 314251876Speter apr_pool_cleanup_null); 315251876Speter 316251876Speter *reslist = rl; 317251876Speter 318251876Speter return APR_SUCCESS; 319251876Speter} 320251876Speter 321251876SpeterAPU_DECLARE(apr_status_t) apr_reslist_destroy(apr_reslist_t *reslist) 322251876Speter{ 323251876Speter return apr_pool_cleanup_run(reslist->pool, reslist, reslist_cleanup); 324251876Speter} 325251876Speter 326251876SpeterAPU_DECLARE(apr_status_t) apr_reslist_acquire(apr_reslist_t *reslist, 327251876Speter void **resource) 328251876Speter{ 329251876Speter apr_status_t rv; 330251876Speter apr_res_t *res; 331251876Speter apr_time_t now; 332251876Speter 333251876Speter#if APR_HAS_THREADS 334251876Speter apr_thread_mutex_lock(reslist->listlock); 335251876Speter#endif 336251876Speter /* If there are idle resources on the available list, use 337251876Speter * them right away. */ 338251876Speter now = apr_time_now(); 339251876Speter while (reslist->nidle > 0) { 340251876Speter /* Pop off the first resource */ 341251876Speter res = pop_resource(reslist); 342251876Speter if (reslist->ttl && (now - res->freed >= reslist->ttl)) { 343251876Speter /* this res is expired - kill it */ 344251876Speter reslist->ntotal--; 345251876Speter rv = destroy_resource(reslist, res); 346251876Speter free_container(reslist, res); 347251876Speter if (rv != APR_SUCCESS) { 348251876Speter#if APR_HAS_THREADS 349251876Speter apr_thread_mutex_unlock(reslist->listlock); 350251876Speter#endif 351251876Speter return rv; /* FIXME: this might cause unnecessary fails */ 352251876Speter } 353251876Speter continue; 354251876Speter } 355251876Speter *resource = res->opaque; 356251876Speter free_container(reslist, res); 357251876Speter#if APR_HAS_THREADS 358251876Speter apr_thread_mutex_unlock(reslist->listlock); 359251876Speter#endif 360251876Speter return APR_SUCCESS; 361251876Speter } 362251876Speter /* If we've hit our max, block until we're allowed to create 363251876Speter * a new one, or something becomes free. */ 364251876Speter while (reslist->ntotal >= reslist->hmax && reslist->nidle <= 0) { 365251876Speter#if APR_HAS_THREADS 366251876Speter if (reslist->timeout) { 367251876Speter if ((rv = apr_thread_cond_timedwait(reslist->avail, 368251876Speter reslist->listlock, reslist->timeout)) != APR_SUCCESS) { 369251876Speter apr_thread_mutex_unlock(reslist->listlock); 370251876Speter return rv; 371251876Speter } 372251876Speter } 373251876Speter else { 374251876Speter apr_thread_cond_wait(reslist->avail, reslist->listlock); 375251876Speter } 376251876Speter#else 377251876Speter return APR_EAGAIN; 378251876Speter#endif 379251876Speter } 380251876Speter /* If we popped out of the loop, first try to see if there 381251876Speter * are new resources available for immediate use. */ 382251876Speter if (reslist->nidle > 0) { 383251876Speter res = pop_resource(reslist); 384251876Speter *resource = res->opaque; 385251876Speter free_container(reslist, res); 386251876Speter#if APR_HAS_THREADS 387251876Speter apr_thread_mutex_unlock(reslist->listlock); 388251876Speter#endif 389251876Speter return APR_SUCCESS; 390251876Speter } 391251876Speter /* Otherwise the reason we dropped out of the loop 392251876Speter * was because there is a new slot available, so create 393251876Speter * a resource to fill the slot and use it. */ 394251876Speter else { 395251876Speter rv = create_resource(reslist, &res); 396251876Speter if (rv == APR_SUCCESS) { 397251876Speter reslist->ntotal++; 398251876Speter *resource = res->opaque; 399251876Speter } 400251876Speter free_container(reslist, res); 401251876Speter#if APR_HAS_THREADS 402251876Speter apr_thread_mutex_unlock(reslist->listlock); 403251876Speter#endif 404251876Speter return rv; 405251876Speter } 406251876Speter} 407251876Speter 408251876SpeterAPU_DECLARE(apr_status_t) apr_reslist_release(apr_reslist_t *reslist, 409251876Speter void *resource) 410251876Speter{ 411251876Speter apr_res_t *res; 412251876Speter 413251876Speter#if APR_HAS_THREADS 414251876Speter apr_thread_mutex_lock(reslist->listlock); 415251876Speter#endif 416251876Speter res = get_container(reslist); 417251876Speter res->opaque = resource; 418251876Speter push_resource(reslist, res); 419251876Speter#if APR_HAS_THREADS 420251876Speter apr_thread_cond_signal(reslist->avail); 421251876Speter apr_thread_mutex_unlock(reslist->listlock); 422251876Speter#endif 423251876Speter 424251876Speter return apr_reslist_maintain(reslist); 425251876Speter} 426251876Speter 427251876SpeterAPU_DECLARE(void) apr_reslist_timeout_set(apr_reslist_t *reslist, 428251876Speter apr_interval_time_t timeout) 429251876Speter{ 430251876Speter reslist->timeout = timeout; 431251876Speter} 432251876Speter 433251876SpeterAPU_DECLARE(apr_uint32_t) apr_reslist_acquired_count(apr_reslist_t *reslist) 434251876Speter{ 435251876Speter apr_uint32_t count; 436251876Speter 437251876Speter#if APR_HAS_THREADS 438251876Speter apr_thread_mutex_lock(reslist->listlock); 439251876Speter#endif 440251876Speter count = reslist->ntotal - reslist->nidle; 441251876Speter#if APR_HAS_THREADS 442251876Speter apr_thread_mutex_unlock(reslist->listlock); 443251876Speter#endif 444251876Speter 445251876Speter return count; 446251876Speter} 447251876Speter 448251876SpeterAPU_DECLARE(apr_status_t) apr_reslist_invalidate(apr_reslist_t *reslist, 449251876Speter void *resource) 450251876Speter{ 451251876Speter apr_status_t ret; 452251876Speter#if APR_HAS_THREADS 453251876Speter apr_thread_mutex_lock(reslist->listlock); 454251876Speter#endif 455251876Speter ret = reslist->destructor(resource, reslist->params, reslist->pool); 456251876Speter reslist->ntotal--; 457251876Speter#if APR_HAS_THREADS 458251876Speter apr_thread_cond_signal(reslist->avail); 459251876Speter apr_thread_mutex_unlock(reslist->listlock); 460251876Speter#endif 461251876Speter return ret; 462251876Speter} 463251876Speter 464251876SpeterAPU_DECLARE(void) apr_reslist_cleanup_order_set(apr_reslist_t *rl, 465251876Speter apr_uint32_t mode) 466251876Speter{ 467251876Speter apr_pool_cleanup_kill(rl->pool, rl, reslist_cleanup); 468251876Speter if (mode == APR_RESLIST_CLEANUP_FIRST) 469251876Speter apr_pool_pre_cleanup_register(rl->pool, rl, reslist_cleanup); 470251876Speter else 471251876Speter apr_pool_cleanup_register(rl->pool, rl, reslist_cleanup, 472251876Speter apr_pool_cleanup_null); 473251876Speter} 474