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
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 "mod_perl.h"
18
19 #ifdef USE_ITHREADS
20
21 /*
22 * tipool == "thread item pool"
23 * this module is intended to provide generic stuctures/functions
24 * for managing a "pool" of a given items (data structures) within a threaded
25 * process. at the moment, mod_perl uses this module to manage a pool
26 * of PerlInterpreter objects. it should be quite easy to reuse for
27 * other data, such as database connection handles and the like.
28 * while it is "generic" it is also tuned for Apache, making use of
29 * apr_pool_t and the like, and implementing start/max/{min,max}_spare/
30 * max_requests configuration.
31 * this is another "proof-of-concept", plenty of room for improvement here
32 */
33
modperl_list_new()34 modperl_list_t *modperl_list_new()
35 {
36 modperl_list_t *listp =
37 (modperl_list_t *)malloc(sizeof(*listp));
38 memset(listp, '\0', sizeof(*listp));
39 return listp;
40 }
41
modperl_list_last(modperl_list_t * list)42 modperl_list_t *modperl_list_last(modperl_list_t *list)
43 {
44 while (list->next) {
45 list = list->next;
46 }
47
48 return list;
49 }
50
modperl_list_first(modperl_list_t * list)51 modperl_list_t *modperl_list_first(modperl_list_t *list)
52 {
53 while (list->prev) {
54 list = list->prev;
55 }
56
57 return list;
58 }
59
modperl_list_append(modperl_list_t * list,modperl_list_t * new_list)60 modperl_list_t *modperl_list_append(modperl_list_t *list,
61 modperl_list_t *new_list)
62 {
63 modperl_list_t *last;
64
65 new_list->prev = new_list->next = NULL;
66
67 if (!list) {
68 return new_list;
69 }
70
71 last = modperl_list_last(list);
72
73 last->next = new_list;
74 new_list->prev = last;
75
76 return list;
77 }
78
modperl_list_prepend(modperl_list_t * list,modperl_list_t * new_list)79 modperl_list_t *modperl_list_prepend(modperl_list_t *list,
80 modperl_list_t *new_list)
81 {
82 new_list->prev = new_list->next = NULL;
83
84 if (!list) {
85 return new_list;
86 }
87
88 if (list->prev) {
89 list->prev->next = new_list;
90 new_list->prev = list->prev;
91 }
92
93 list->prev = new_list;
94 new_list->next = list;
95
96 return new_list;
97 }
98
modperl_list_remove(modperl_list_t * list,modperl_list_t * rlist)99 modperl_list_t *modperl_list_remove(modperl_list_t *list,
100 modperl_list_t *rlist)
101 {
102 modperl_list_t *tmp = list;
103
104 while (tmp) {
105 if (tmp != rlist) {
106 tmp = tmp->next;
107 }
108 else {
109 if (tmp->prev) {
110 tmp->prev->next = tmp->next;
111 }
112 if (tmp->next) {
113 tmp->next->prev = tmp->prev;
114 }
115 if (list == tmp) {
116 list = list->next;
117 }
118
119 break;
120 }
121 }
122
123 #ifdef MP_TRACE
124 if (!tmp) {
125 /* should never happen */
126 MP_TRACE_i(MP_FUNC, "failed to find 0x%lx in list 0x%lx",
127 (unsigned long)rlist, (unsigned long)list);
128 }
129 #endif
130
131 return list;
132 }
133
modperl_list_remove_data(modperl_list_t * list,void * data,modperl_list_t ** listp)134 modperl_list_t *modperl_list_remove_data(modperl_list_t *list,
135 void *data,
136 modperl_list_t **listp)
137 {
138 modperl_list_t *tmp = list;
139
140 while (tmp) {
141 if (tmp->data != data) {
142 tmp = tmp->next;
143 }
144 else {
145 *listp = tmp;
146 if (tmp->prev) {
147 tmp->prev->next = tmp->next;
148 }
149 if (tmp->next) {
150 tmp->next->prev = tmp->prev;
151 }
152 if (list == tmp) {
153 list = list->next;
154 }
155
156 break;
157 }
158 }
159
160 return list;
161 }
162
modperl_tipool_new(apr_pool_t * p,modperl_tipool_config_t * cfg,modperl_tipool_vtbl_t * func,void * data)163 modperl_tipool_t *modperl_tipool_new(apr_pool_t *p,
164 modperl_tipool_config_t *cfg,
165 modperl_tipool_vtbl_t *func,
166 void *data)
167 {
168 modperl_tipool_t *tipool =
169 (modperl_tipool_t *)apr_pcalloc(p, sizeof(*tipool));
170
171 tipool->cfg = cfg;
172 tipool->func = func;
173 tipool->data = data;
174
175 MUTEX_INIT(&tipool->tiplock);
176 COND_INIT(&tipool->available);
177
178 return tipool;
179 }
180
modperl_tipool_init(modperl_tipool_t * tipool)181 void modperl_tipool_init(modperl_tipool_t *tipool)
182 {
183 int i;
184
185 for (i=0; i<tipool->cfg->start; i++) {
186 void *item =
187 (*tipool->func->tipool_sgrow)(tipool, tipool->data);
188
189 modperl_tipool_add(tipool, item);
190 }
191
192 MP_TRACE_i(MP_FUNC, "start=%d, max=%d, min_spare=%d, max_spare=%d",
193 tipool->cfg->start, tipool->cfg->max,
194 tipool->cfg->min_spare, tipool->cfg->max_spare);
195
196 }
197
modperl_tipool_destroy(modperl_tipool_t * tipool)198 void modperl_tipool_destroy(modperl_tipool_t *tipool)
199 {
200 while (tipool->idle) {
201 modperl_list_t *listp;
202
203 if (tipool->func->tipool_destroy) {
204 (*tipool->func->tipool_destroy)(tipool, tipool->data,
205 tipool->idle->data);
206 }
207 tipool->size--;
208 listp = tipool->idle->next;
209 free(tipool->idle);
210 tipool->idle = listp;
211 }
212
213 if (tipool->busy) {
214 MP_TRACE_i(MP_FUNC, "ERROR: %d items still in use",
215 tipool->in_use);
216 }
217
218 MUTEX_DESTROY(&tipool->tiplock);
219 COND_DESTROY(&tipool->available);
220 }
221
modperl_tipool_add(modperl_tipool_t * tipool,void * data)222 void modperl_tipool_add(modperl_tipool_t *tipool, void *data)
223 {
224 modperl_list_t *listp = modperl_list_new();
225
226 listp->data = data;
227
228 /* assuming tipool->tiplock has already been acquired */
229
230 tipool->idle = modperl_list_append(tipool->idle, listp);
231
232 tipool->size++;
233
234 MP_TRACE_i(MP_FUNC, "added 0x%lx (size=%d)",
235 (unsigned long)listp, tipool->size);
236 }
237
modperl_tipool_remove(modperl_tipool_t * tipool,modperl_list_t * listp)238 void modperl_tipool_remove(modperl_tipool_t *tipool, modperl_list_t *listp)
239 {
240 /* assuming tipool->tiplock has already been acquired */
241
242 tipool->idle = modperl_list_remove(tipool->idle, listp);
243
244 tipool->size--;
245 MP_TRACE_i(MP_FUNC, "removed 0x%lx (size=%d)",
246 (unsigned long)listp, tipool->size);
247 }
248
modperl_tipool_pop(modperl_tipool_t * tipool)249 modperl_list_t *modperl_tipool_pop(modperl_tipool_t *tipool)
250 {
251 modperl_list_t *head;
252
253 modperl_tipool_lock(tipool);
254
255 if (tipool->size == tipool->in_use) {
256 if (tipool->size < tipool->cfg->max) {
257 MP_TRACE_i(MP_FUNC,
258 "no idle items, size %d < %d max",
259 tipool->size, tipool->cfg->max);
260 if (tipool->func->tipool_rgrow) {
261 void * item =
262 (*tipool->func->tipool_rgrow)(tipool, tipool->data);
263
264 modperl_tipool_add(tipool, item);
265 }
266 }
267 /* block until an item becomes available */
268 modperl_tipool_wait(tipool);
269 }
270
271 head = tipool->idle;
272
273 tipool->idle = modperl_list_remove(tipool->idle, head);
274 tipool->busy = modperl_list_append(tipool->busy, head);
275
276 tipool->in_use++;
277
278 /* XXX: this should never happen */
279 if (!head) {
280 MP_TRACE_i(MP_FUNC, "PANIC: no items available, %d of %d in use",
281 tipool->in_use, tipool->size);
282 abort();
283 }
284
285 modperl_tipool_unlock(tipool);
286
287 return head;
288 }
289
modperl_tipool_putback_base(modperl_tipool_t * tipool,modperl_list_t * listp,void * data,int num_requests)290 static void modperl_tipool_putback_base(modperl_tipool_t *tipool,
291 modperl_list_t *listp,
292 void *data,
293 int num_requests)
294 {
295 int max_spare, max_requests;
296
297 modperl_tipool_lock(tipool);
298
299 /* remove from busy list, add back to idle */
300 /* XXX: option to sort list, e.g. on num_requests */
301
302 if (listp) {
303 tipool->busy = modperl_list_remove(tipool->busy, listp);
304 }
305 else {
306 tipool->busy = modperl_list_remove_data(tipool->busy, data, &listp);
307 }
308
309 if (!listp) {
310 /* XXX: Attempt to putback something that was never there */
311 modperl_tipool_unlock(tipool);
312 return;
313 }
314
315 tipool->idle = modperl_list_prepend(tipool->idle, listp);
316
317 tipool->in_use--;
318
319 #ifdef MP_TRACE
320 if (!tipool->busy && tipool->func->tipool_dump) {
321 MP_TRACE_i(MP_FUNC, "all items idle:");
322 MP_TRACE_i_do((*tipool->func->tipool_dump)(tipool,
323 tipool->data,
324 tipool->idle));
325 }
326 #endif
327
328 MP_TRACE_i(MP_FUNC, "0x%lx now available (%d in use, %d running)",
329 (unsigned long)listp->data, tipool->in_use, tipool->size);
330
331 modperl_tipool_broadcast(tipool);
332 if (tipool->in_use == (tipool->cfg->max - 1)) {
333 /* hurry up, another thread may be blocking */
334 modperl_tipool_unlock(tipool);
335 return;
336 }
337
338 max_spare = ((tipool->size - tipool->in_use) > tipool->cfg->max_spare);
339 max_requests = ((num_requests > 0) &&
340 (num_requests > tipool->cfg->max_requests));
341
342 if (max_spare) {
343 MP_TRACE_i(MP_FUNC,
344 "shrinking pool: max_spare=%d, only %d of %d in use",
345 tipool->cfg->max_spare, tipool->in_use, tipool->size);
346 }
347 else if (max_requests) {
348 MP_TRACE_i(MP_FUNC, "shrinking pool: max requests %d reached",
349 tipool->cfg->max_requests);
350 }
351
352 /* XXX: this management should probably be happening elsewhere
353 * like in a thread spawned at startup
354 */
355 if (max_spare || max_requests) {
356 modperl_tipool_remove(tipool, listp);
357
358 if (tipool->func->tipool_destroy) {
359 (*tipool->func->tipool_destroy)(tipool, tipool->data,
360 listp->data);
361 }
362
363 free(listp); /* gone for good */
364
365 if (max_requests && ((tipool->size - tipool->in_use) <
366 tipool->cfg->min_spare)) {
367 if (tipool->func->tipool_rgrow) {
368 void *item =
369 (*tipool->func->tipool_rgrow)(tipool,
370 tipool->data);
371
372 MP_TRACE_i(MP_FUNC,
373 "growing pool: min_spare=%d, %d of %d in use",
374 tipool->cfg->min_spare, tipool->in_use,
375 tipool->size);
376
377 modperl_tipool_add(tipool, item);
378 }
379 }
380 }
381
382 modperl_tipool_unlock(tipool);
383 }
384
385 /* _data functions are so structures (e.g. modperl_interp_t) don't
386 * need to maintain a pointer back to the modperl_list_t
387 */
388
modperl_tipool_putback_data(modperl_tipool_t * tipool,void * data,int num_requests)389 void modperl_tipool_putback_data(modperl_tipool_t *tipool,
390 void *data,
391 int num_requests)
392 {
393 modperl_tipool_putback_base(tipool, NULL, data, num_requests);
394 }
395
modperl_tipool_putback(modperl_tipool_t * tipool,modperl_list_t * listp,int num_requests)396 void modperl_tipool_putback(modperl_tipool_t *tipool,
397 modperl_list_t *listp,
398 int num_requests)
399 {
400 modperl_tipool_putback_base(tipool, listp, NULL, num_requests);
401 }
402
403 #endif /* USE_ITHREADS */
404
405 /*
406 * Local Variables:
407 * c-basic-offset: 4
408 * indent-tabs-mode: nil
409 * End:
410 */
411