static void clear_pool(struct pool_rec *p) { /* Sanity check. */ if (p == NULL) { return; } pr_alarms_block(); /* Run through any cleanups. */ run_cleanups(p->cleanups); p->cleanups = NULL; /* Destroy subpools. */ while (p->sub_pools) { destroy_pool(p->sub_pools); } p->sub_pools = NULL; free_blocks(p->first->h.next, p->tag); p->first->h.next = NULL; p->last = p->first; p->first->h.first_avail = p->free_first_avail; pr_alarms_unblock(); }
void destroy_pool(pool *p) { if (p == NULL) { return; } pr_alarms_block(); if (p->parent) { if (p->parent->sub_pools == p) { p->parent->sub_pools = p->sub_next; } if (p->sub_prev) { p->sub_prev->sub_next = p->sub_next; } if (p->sub_next) { p->sub_next->sub_prev = p->sub_prev; } } clear_pool(p); free_blocks(p->first, p->tag); pr_alarms_unblock(); #ifdef PR_DEVEL_NO_POOL_FREELIST /* If configured explicitly to do so, call free(3) on the freelist after * a pool is destroyed. This can be useful for tracking down use-after-free * and other memory issues using libraries such as dmalloc. */ pool_release_free_block_list(); #endif /* PR_EVEL_NO_POOL_FREELIST */ }
struct pool *make_sub_pool(struct pool *p) { union block_hdr *blok; pool *new_pool; pr_alarms_block(); blok = new_block(0, FALSE); new_pool = (pool *) blok->h.first_avail; blok->h.first_avail += POOL_HDR_BYTES; memset(new_pool, 0, sizeof(struct pool)); new_pool->free_first_avail = blok->h.first_avail; new_pool->first = new_pool->last = blok; if (p) { new_pool->parent = p; new_pool->sub_next = p->sub_pools; if (new_pool->sub_next) new_pool->sub_next->sub_prev = new_pool; p->sub_pools = new_pool; } pr_alarms_unblock(); return new_pool; }
struct pool_rec *pr_pool_create_sz(struct pool_rec *p, size_t sz) { union block_hdr *blok; pool *new_pool; pr_alarms_block(); blok = new_block(sz + POOL_HDR_BYTES, TRUE); new_pool = (pool *) blok->h.first_avail; blok->h.first_avail += POOL_HDR_BYTES; memset(new_pool, 0, sizeof(struct pool_rec)); new_pool->free_first_avail = blok->h.first_avail; new_pool->first = new_pool->last = blok; if (p) { new_pool->parent = p; new_pool->sub_next = p->sub_pools; if (new_pool->sub_next) new_pool->sub_next->sub_prev = new_pool; p->sub_pools = new_pool; } pr_alarms_unblock(); return new_pool; }
/* Release the entire free block list */ static void pool_release_free_block_list(void) { union block_hdr *blok = NULL, *next = NULL; pr_alarms_block(); for (blok = block_freelist; blok; blok = next) { next = blok->h.next; free(blok); } block_freelist = NULL; pr_alarms_unblock(); }
/* Release the entire free block list */ static void pool_release_free_block_list(void) { union block_hdr *blok,*next; pr_alarms_block(); blok = block_freelist; if (blok) { for (next = blok->h.next; next; blok = next, next = blok->h.next) free(blok); } block_freelist = NULL; pr_alarms_unblock(); }
int pr_timer_reset(int timerno, module *mod) { struct timer *t = NULL; if (!timers) { errno = EPERM; return -1; } if (_indispatch) { errno = EINTR; return -1; } pr_alarms_block(); if (!recycled) recycled = xaset_create(timer_pool, NULL); for (t = (struct timer *) timers->xas_list; t; t = t->next) { if (t->timerno == timerno && (t->mod == mod || mod == ANY_MODULE)) { t->count = t->interval; xaset_remove(timers, (xasetmember_t *) t); xaset_insert(recycled, (xasetmember_t *) t); nalarms++; /* The handle_alarm() function also readjusts the timers lists * as part of its processing, so it needs to be called when a timer * is reset. */ handle_alarm(); break; } } pr_alarms_unblock(); if (t != NULL) { pr_trace_msg("timer", 7, "reset timer ID %d ('%s', for module '%s')", t->timerno, t->desc, t->mod ? t->mod->name : "[none]"); return t->timerno; } return 0; }
END_TEST START_TEST (timer_sleep_test) { int res; res = pr_timer_sleep(0); fail_unless(res == -1, "Failed to handle sleep len of zero"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); pr_alarms_block(); res = pr_timer_sleep(1); fail_unless(res == -1, "Failed to handle blocked alarms when sleeping"); fail_unless(errno == EPERM, "Failed to set errno to EPERM"); pr_alarms_unblock(); res = pr_timer_sleep(1); fail_unless(res == 0, "Failed to sleep: %s", strerror(errno)); }
static void *alloc_pool(struct pool_rec *p, size_t reqsz, int exact) { /* Round up requested size to an even number of aligned units */ size_t nclicks = 1 + ((reqsz - 1) / CLICK_SZ); size_t sz = nclicks * CLICK_SZ; /* For performance, see if space is available in the most recently * allocated block. */ union block_hdr *blok = p->last; char *first_avail = blok->h.first_avail; char *new_first_avail; if (reqsz == 0) { /* Don't try to allocate memory of zero length. * * This should NOT happen normally; if it does, by returning NULL we * almost guarantee a null pointer dereference. */ errno = EINVAL; return NULL; } new_first_avail = first_avail + sz; if (new_first_avail <= (char *) blok->h.endp) { blok->h.first_avail = new_first_avail; return (void *) first_avail; } /* Need a new one that's big enough */ pr_alarms_block(); blok = new_block(sz, exact); p->last->h.next = blok; p->last = blok; first_avail = blok->h.first_avail; blok->h.first_avail = sz + (char *) blok->h.first_avail; pr_alarms_unblock(); return (void *) first_avail; }
void destroy_pool(pool *p) { if (p == NULL) return; pr_alarms_block(); if (p->parent) { if (p->parent->sub_pools == p) p->parent->sub_pools = p->sub_next; if (p->sub_prev) p->sub_prev->sub_next = p->sub_next; if (p->sub_next) p->sub_next->sub_prev = p->sub_prev; } clear_pool(p); free_blocks(p->first, p->tag); pr_alarms_unblock(); }
static void *alloc_pool(struct pool_rec *p, size_t reqsz, int exact) { /* Round up requested size to an even number of aligned units */ size_t nclicks = 1 + ((reqsz - 1) / CLICK_SZ); size_t sz = nclicks * CLICK_SZ; /* For performance, see if space is available in the most recently * allocated block. */ union block_hdr *blok = p->last; char *first_avail = blok->h.first_avail; char *new_first_avail; if (reqsz == 0) { /* Don't try to allocate memory of zero length. */ return NULL; } new_first_avail = first_avail + sz; if (new_first_avail <= blok->h.endp) { blok->h.first_avail = new_first_avail; return (void *) first_avail; } /* Need a new one that's big enough */ pr_alarms_block(); blok = new_block(sz, exact); p->last->h.next = blok; p->last = blok; first_avail = blok->h.first_avail; blok->h.first_avail += sz; pr_alarms_unblock(); return (void *) first_avail; }
/* This function does the work of iterating through the list of registered * timers, checking to see if their callbacks should be invoked and whether * they should be removed from the registration list. Its return value is * the amount of time remaining on the first timer in the list. */ static int process_timers(int elapsed) { struct timer *t = NULL, *next = NULL; if (!recycled) recycled = xaset_create(timer_pool, NULL); if (!elapsed && !recycled->xas_list) { if (!timers) return 0; return (timers->xas_list ? ((struct timer *) timers->xas_list)->count : 0); } /* Critical code, no interruptions please */ if (_indispatch) return 0; pr_alarms_block(); _indispatch++; if (elapsed) { for (t = (struct timer *) timers->xas_list; t; t = next) { /* If this timer has already been handled, skip */ next = t->next; if (t->remove) { /* Move the timer onto the free_timers chain, for later reuse. */ xaset_remove(timers, (xasetmember_t *) t); xaset_insert(free_timers, (xasetmember_t *) t); } else if ((t->count -= elapsed) <= 0) { /* This timer's interval has elapsed, so trigger its callback. */ pr_trace_msg("timer", 4, "%ld %s for timer ID %d ('%s', for module '%s') elapsed, invoking " "callback (%p)", t->interval, t->interval != 1 ? "seconds" : "second", t->timerno, t->desc ? t->desc : "<unknown>", t->mod ? t->mod->name : "<none>", t->callback); if (t->callback(t->interval, t->timerno, t->interval - t->count, t->mod) == 0) { /* A return value of zero means this timer is done, and can be * removed. */ xaset_remove(timers, (xasetmember_t *) t); xaset_insert(free_timers, (xasetmember_t *) t); } else { /* A non-zero return value from a timer callback signals that * the timer should be reused/restarted. */ pr_trace_msg("timer", 6, "restarting timer ID %d ('%s'), as per " "callback", t->timerno, t->desc ? t->desc : "<unknown>"); xaset_remove(timers, (xasetmember_t *) t); t->count = t->interval; xaset_insert(recycled, (xasetmember_t *) t); } } } } /* Put the recycled timers back into the main timer list. */ while ((t = (struct timer *) recycled->xas_list) != NULL) { xaset_remove(recycled, (xasetmember_t *) t); xaset_insert_sort(timers, (xasetmember_t *) t, TRUE); } _indispatch--; pr_alarms_unblock(); /* If no active timers remain in the list, there is no reason to set the * SIGALRM handle. */ return (timers->xas_list ? ((struct timer *) timers->xas_list)->count : 0); }
int pr_timer_add(int seconds, int timerno, module *mod, callback_t cb, const char *desc) { struct timer *t = NULL; if (seconds <= 0 || cb == NULL || desc == NULL) { errno = EINVAL; return -1; } if (!timers) timers = xaset_create(timer_pool, (XASET_COMPARE) timer_cmp); /* Check to see that, if specified, the timerno is not already in use. */ if (timerno >= 0) { for (t = (struct timer *) timers->xas_list; t; t = t->next) { if (t->timerno == timerno) { errno = EPERM; return -1; } } } if (!free_timers) free_timers = xaset_create(timer_pool, NULL); /* Try to use an old timer first */ pr_alarms_block(); t = (struct timer *) free_timers->xas_list; if (t != NULL) { xaset_remove(free_timers, (xasetmember_t *) t); } else { if (timer_pool == NULL) { timer_pool = make_sub_pool(permanent_pool); pr_pool_tag(timer_pool, "Timer Pool"); } /* Must allocate a new one */ t = palloc(timer_pool, sizeof(struct timer)); } if (timerno < 0) { /* Dynamic timer */ if (dynamic_timerno < PR_TIMER_DYNAMIC_TIMERNO) { dynamic_timerno = PR_TIMER_DYNAMIC_TIMERNO; } timerno = dynamic_timerno++; } t->timerno = timerno; t->count = t->interval = seconds; t->callback = cb; t->mod = mod; t->remove = 0; t->desc = desc; /* If called while _indispatch, add to the recycled list to prevent * list corruption */ if (_indispatch) { if (!recycled) recycled = xaset_create(timer_pool, NULL); xaset_insert(recycled, (xasetmember_t *) t); } else { xaset_insert_sort(timers, (xasetmember_t *) t, TRUE); nalarms++; set_sig_alarm(); /* The handle_alarm() function also readjusts the timers lists * as part of its processing, so it needs to be called when a timer * is added. */ handle_alarm(); } pr_alarms_unblock(); pr_trace_msg("timer", 7, "added timer ID %d ('%s', for module '%s'), " "triggering in %ld %s", t->timerno, t->desc, t->mod ? t->mod->name : "[none]", t->interval, t->interval != 1 ? "seconds" : "second"); return timerno; }
int pr_timer_remove(int timerno, module *mod) { struct timer *t = NULL, *tnext = NULL; int nremoved = 0; /* If there are no timers currently registered, do nothing. */ if (!timers) return 0; pr_alarms_block(); for (t = (struct timer *) timers->xas_list; t; t = tnext) { tnext = t->next; if ((timerno < 0 || t->timerno == timerno) && (mod == ANY_MODULE || t->mod == mod)) { nremoved++; if (_indispatch) { t->remove++; } else { xaset_remove(timers, (xasetmember_t *) t); xaset_insert(free_timers, (xasetmember_t *) t); nalarms++; /* The handle_alarm() function also readjusts the timers lists * as part of its processing, so it needs to be called when a timer * is removed. */ handle_alarm(); } pr_trace_msg("timer", 7, "removed timer ID %d ('%s', for module '%s')", t->timerno, t->desc, t->mod ? t->mod->name : "[none]"); } /* If we are removing a specific timer, break out of the loop now. * Otherwise, keep removing any matching timers. */ if (nremoved > 0 && timerno >= 0) { break; } } pr_alarms_unblock(); if (nremoved == 0) { errno = ENOENT; return -1; } /* If we removed a specific timer because of the given timerno, return * that timerno value. */ if (timerno >= 0) { return timerno; } return nremoved; }