2
0

apr_reslist.c 11 KB

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