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