Newer
Older
lock_info->locks[i].times_locked,
lock_info->locks[i].suspended ? " - suspended" : "");
#ifdef HAVE_BKTR
append_backtrace_information(str, lock_info->locks[i].backtrace);
#endif
if (!lock_info->locks[i].pending || lock_info->locks[i].pending == -1)
return;
/* We only have further details for mutexes right now */
if (lock_info->locks[i].type != AST_MUTEX)
return;
ast_reentrancy_lock(lt);
for (j = 0; *str && j < lt->reentrancy; j++) {
lt->file[j], lt->lineno[j], lt->func[j]);
/*! This function can help you find highly temporal locks; locks that happen for a
short time, but at unexpected times, usually at times that create a deadlock,
Why is this thing locked right then? Who is locking it? Who am I fighting
To answer such questions, just call this routine before you would normally try
to aquire a lock. It doesn't do anything if the lock is not acquired. If the
lock is taken, it will publish a line or two to the console via ast_log().
Sometimes, the lock message is pretty uninformative. For instance, you might
find that the lock is being aquired deep within the astobj2 code; this tells
you little about higher level routines that call the astobj2 routines.
But, using gdb, you can set a break at the ast_log below, and for that
breakpoint, you can set the commands:
where
cont
which will give a stack trace and continue. -- that aught to do the job!
*/
void ast_log_show_lock(void *this_lock_addr)
Steve Murphy
committed
{
struct thr_lock_info *lock_info;
struct ast_str *str;
if (!(str = ast_str_create(4096))) {
ast_log(LOG_NOTICE,"Could not create str\n");
return;
}
Steve Murphy
committed
pthread_mutex_lock(&lock_infos_lock.mutex);
AST_LIST_TRAVERSE(&lock_infos, lock_info, entry) {
int i;
pthread_mutex_lock(&lock_info->lock);
for (i = 0; str && i < lock_info->num_locks; i++) {
/* ONLY show info about this particular lock, if
it's acquired... */
Steve Murphy
committed
if (lock_info->locks[i].lock_addr == this_lock_addr) {
ast_log(LOG_NOTICE, "%s", ast_str_buffer(str));
Steve Murphy
committed
}
}
pthread_mutex_unlock(&lock_info->lock);
}
pthread_mutex_unlock(&lock_infos_lock.mutex);
Steve Murphy
committed
}
struct ast_str *ast_dump_locks(void)
{
struct thr_lock_info *lock_info;
if (!(str = ast_str_create(4096))) {
"=======================================================================\n"
"=== %s\n"
"=== Currently Held Locks\n"
"=======================================================================\n"
"===\n"
Steve Murphy
committed
"=== <pending> <lock#> (<file>): <lock type> <line num> <function> <lock name> <lock addr> (times locked)\n"
if (!str) {
return NULL;
}
pthread_mutex_lock(&lock_infos_lock.mutex);
AST_LIST_TRAVERSE(&lock_infos, lock_info, entry) {
int i;
int header_printed = 0;
pthread_mutex_lock(&lock_info->lock);
for (i = 0; str && i < lock_info->num_locks; i++) {
/* Don't show suspended locks */
if (lock_info->locks[i].suspended) {
continue;
if (!header_printed) {
if (lock_info->lwp != -1) {
ast_str_append(&str, 0, "=== Thread ID: 0x%lx LWP:%d (%s)\n",
(long unsigned) lock_info->thread_id, lock_info->lwp, lock_info->thread_name);
} else {
ast_str_append(&str, 0, "=== Thread ID: 0x%lx (%s)\n",
(long unsigned) lock_info->thread_id, lock_info->thread_name);
header_printed = 1;
}
append_lock_information(&str, lock_info, i);
}
pthread_mutex_unlock(&lock_info->lock);
if (!str) {
break;
}
if (header_printed) {
ast_str_append(&str, 0, "=== -------------------------------------------------------------------\n"
"===\n");
}
if (!str) {
break;
}
}
pthread_mutex_unlock(&lock_infos_lock.mutex);
if (!str) {
return NULL;
}
ast_str_append(&str, 0, "=======================================================================\n"
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
return str;
}
static char *handle_show_locks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ast_str *str;
switch (cmd) {
case CLI_INIT:
e->command = "core show locks";
e->usage =
"Usage: core show locks\n"
" This command is for lock debugging. It prints out which locks\n"
"are owned by each active thread.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
str = ast_dump_locks();
if (!str) {
ast_cli(a->fd, "%s", ast_str_buffer(str));
Tilghman Lesher
committed
ast_free(str);
}
static struct ast_cli_entry utils_cli[] = {
Jason Parker
committed
AST_CLI_DEFINE(handle_show_locks, "Show which locks are held by which thread"),
};
#endif /* DEBUG_THREADS */
/*
* support for 'show threads'. The start routine is wrapped by
* dummy_start(), so that ast_register_thread() and
* ast_unregister_thread() know the thread identifier.
*/
struct thr_arg {
void *(*start_routine)(void *);
void *data;
char *name;
};
/*
* on OS/X, pthread_cleanup_push() and pthread_cleanup_pop()
* are odd macros which start and end a block, so they _must_ be
* used in pairs (the latter with a '1' argument to call the
* handler on exit.
* On BSD we don't need this, but we keep it for compatibility.
*/
static void *dummy_start(void *data)
struct thr_arg a = *((struct thr_arg *) data); /* make a local copy */
#ifdef DEBUG_THREADS
struct thr_lock_info *lock_info;
pthread_mutexattr_t mutex_attr;
if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
return NULL;
lock_info->thread_id = pthread_self();
lock_info->lwp = ast_get_tid();
lock_info->thread_name = strdup(a.name);
pthread_mutexattr_init(&mutex_attr);
pthread_mutexattr_settype(&mutex_attr, AST_MUTEX_KIND);
pthread_mutex_init(&lock_info->lock, &mutex_attr);
pthread_mutexattr_destroy(&mutex_attr);
pthread_mutex_lock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
AST_LIST_INSERT_TAIL(&lock_infos, lock_info, entry);
pthread_mutex_unlock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
#endif /* DEBUG_THREADS */
Matthew Jordan
committed
/* note that even though data->name is a pointer to allocated memory,
we are not freeing it here because ast_register_thread is going to
keep a copy of the pointer and then ast_unregister_thread will
free the memory
*/
ast_free(data);
ast_register_thread(a.name);
pthread_cleanup_push(ast_unregister_thread, (void *) pthread_self());
ret = a.start_routine(a.data);
#endif /* !LOW_MEMORY */
int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
void *data, size_t stacksize, const char *file, const char *caller,
int line, const char *start_fn)
attr = ast_alloca(sizeof(*attr));
#ifdef __linux__
/* On Linux, pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
which is kind of useless. Change this here to
PTHREAD_INHERIT_SCHED; that way the -p option to set realtime
priority will propagate down to new threads by default.
This does mean that callers cannot set a different priority using
PTHREAD_EXPLICIT_SCHED in the attr argument; instead they must set
the priority afterwards with pthread_setschedparam(). */
if ((errno = pthread_attr_setinheritsched(attr, PTHREAD_INHERIT_SCHED)))
ast_log(LOG_WARNING, "pthread_attr_setinheritsched: %s\n", strerror(errno));
#endif
if (!stacksize)
stacksize = AST_STACKSIZE;
if ((errno = pthread_attr_setstacksize(attr, stacksize ? stacksize : AST_STACKSIZE)))
ast_log(LOG_WARNING, "pthread_attr_setstacksize: %s\n", strerror(errno));
if ((a = ast_malloc(sizeof(*a)))) {
a->start_routine = start_routine;
a->data = data;
start_routine = dummy_start;
if (ast_asprintf(&a->name, "%-20s started at [%5d] %s %s()",
Kevin P. Fleming
committed
start_fn, line, file, caller) < 0) {
a->name = NULL;
}
return pthread_create(thread, attr, start_routine, data); /* We're in ast_pthread_create, so it's okay */
}
Russell Bryant
committed
int ast_pthread_create_detached_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
void *data, size_t stacksize, const char *file, const char *caller,
int line, const char *start_fn)
{
unsigned char attr_destroy = 0;
int res;
if (!attr) {
attr = ast_alloca(sizeof(*attr));
Russell Bryant
committed
pthread_attr_init(attr);
attr_destroy = 1;
}
if ((errno = pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED)))
ast_log(LOG_WARNING, "pthread_attr_setdetachstate: %s\n", strerror(errno));
res = ast_pthread_create_stack(thread, attr, start_routine, data,
Russell Bryant
committed
stacksize, file, caller, line, start_fn);
if (attr_destroy)
pthread_attr_destroy(attr);
return res;
}
int ast_wait_for_input(int fd, int ms)
{
struct pollfd pfd[1];
Richard Mudgett
committed
memset(pfd, 0, sizeof(pfd));
pfd[0].fd = fd;
pfd[0].events = POLLIN | POLLPRI;
return ast_poll(pfd, 1, ms);
}
int ast_wait_for_output(int fd, int ms)
{
struct pollfd pfd[1];
memset(pfd, 0, sizeof(pfd));
pfd[0].fd = fd;
Richard Mudgett
committed
pfd[0].events = POLLOUT;
return ast_poll(pfd, 1, ms);
Richard Mudgett
committed
static int wait_for_output(int fd, int timeoutms)
{
struct pollfd pfd = {
.fd = fd,
.events = POLLOUT,
};
int res;
struct timeval start = ast_tvnow();
int elapsed = 0;
/* poll() until the fd is writable without blocking */
while ((res = ast_poll(&pfd, 1, timeoutms - elapsed)) <= 0) {
if (res == 0) {
/* timed out. */
ast_debug(1, "Timed out trying to write\n");
return -1;
} else if (res == -1) {
/* poll() returned an error, check to see if it was fatal */
if (errno == EINTR || errno == EAGAIN) {
elapsed = ast_tvdiff_ms(ast_tvnow(), start);
if (elapsed >= timeoutms) {
return -1;
}
/* This was an acceptable error, go back into poll() */
continue;
}
/* Fatal error, bail. */
ast_log(LOG_ERROR, "poll returned error: %s\n", strerror(errno));
return -1;
}
elapsed = ast_tvdiff_ms(ast_tvnow(), start);
if (elapsed >= timeoutms) {
return -1;
}
}
return 0;
}
/*!
* Try to write string, but wait no more than ms milliseconds before timing out.
*
* \note The code assumes that the file descriptor has NONBLOCK set,
* so there is only one system call made to do a write, unless we actually
* have a need to wait. This way, we get better performance.
* If the descriptor is blocking, all assumptions on the guaranteed
* detail do not apply anymore.
*/
int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
struct timeval start = ast_tvnow();
int res = 0;
while (len) {
Richard Mudgett
committed
if (wait_for_output(fd, timeoutms - elapsed)) {
return -1;
res = write(fd, s, len);
if (res < 0 && errno != EAGAIN && errno != EINTR) {
/* fatal error from write() */
ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
return -1;
}
if (res < 0) {
/* It was an acceptable error */
res = 0;
}
/* Update how much data we have left to write */
len -= res;
s += res;
res = 0;
elapsed = ast_tvdiff_ms(ast_tvnow(), start);
if (elapsed >= timeoutms) {
* This is only an error condition if we haven't finished writing. */
res = len ? -1 : 0;
break;
}
return res;
}
int ast_careful_fwrite(FILE *f, int fd, const char *src, size_t len, int timeoutms)
{
struct timeval start = ast_tvnow();
int n = 0;
while (len) {
Richard Mudgett
committed
if (wait_for_output(fd, timeoutms - elapsed)) {
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
/* poll returned a fatal error, so bail out immediately. */
return -1;
}
/* Clear any errors from a previous write */
clearerr(f);
n = fwrite(src, 1, len, f);
if (ferror(f) && errno != EINTR && errno != EAGAIN) {
/* fatal error from fwrite() */
if (!feof(f)) {
/* Don't spam the logs if it was just that the connection is closed. */
ast_log(LOG_ERROR, "fwrite() returned error: %s\n", strerror(errno));
}
n = -1;
break;
}
/* Update for data already written to the socket */
len -= n;
src += n;
elapsed = ast_tvdiff_ms(ast_tvnow(), start);
* This is only an error condition if we haven't finished writing. */
n = len ? -1 : 0;
break;
}
}
errno = 0;
while (fflush(f)) {
if (errno == EAGAIN || errno == EINTR) {
/* fflush() does not appear to reset errno if it flushes
* and reaches EOF at the same time. It returns EOF with
* the last seen value of errno, causing a possible loop.
* Also usleep() to reduce CPU eating if it does loop */
errno = 0;
usleep(1);
continue;
}
if (errno && !feof(f)) {
/* Don't spam the logs if it was just that the connection is closed. */
ast_log(LOG_ERROR, "fflush() returned error: %s\n", strerror(errno));
}
n = -1;
break;
}
return n < 0 ? -1 : 0;
}
char *ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
{
char *e;
char *q;
s = ast_strip(s);
if ((q = strchr(beg_quotes, *s)) && *q != '\0') {
e = s + strlen(s) - 1;
if (*e == *(end_quotes + (q - beg_quotes))) {
s++;
*e = '\0';
}
}
return s;
}
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
char *ast_strsep(char **iss, const char sep, uint32_t flags)
{
char *st = *iss;
char *is;
int inquote = 0;
int found = 0;
char stack[8];
if (iss == NULL || *iss == '\0') {
return NULL;
}
memset(stack, 0, sizeof(stack));
for(is = st; *is; is++) {
if (*is == '\\') {
if (*++is != '\0') {
is++;
} else {
break;
}
}
if (*is == '\'' || *is == '"') {
if (*is == stack[inquote]) {
stack[inquote--] = '\0';
} else {
if (++inquote >= sizeof(stack)) {
return NULL;
}
stack[inquote] = *is;
}
}
if (*is == sep && !inquote) {
*is = '\0';
found = 1;
*iss = is + 1;
break;
}
}
if (!found) {
*iss = NULL;
}
if (flags & AST_STRSEP_STRIP) {
st = ast_strip_quoted(st, "'\"", "'\"");
}
if (flags & AST_STRSEP_TRIM) {
st = ast_strip(st);
}
if (flags & AST_STRSEP_UNESCAPE) {
ast_unescape_quoted(st);
}
return st;
}
char *ast_unescape_semicolon(char *s)
{
char *e;
char *work = s;
while ((e = strchr(work, ';'))) {
if ((e > work) && (*(e-1) == '\\')) {
memmove(e - 1, e, strlen(e) + 1);
work = e;
}
}
return s;
}
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
/* !\brief unescape some C sequences in place, return pointer to the original string.
*/
char *ast_unescape_c(char *src)
{
char c, *ret, *dst;
if (src == NULL)
return NULL;
for (ret = dst = src; (c = *src++); *dst++ = c ) {
if (c != '\\')
continue; /* copy char at the end of the loop */
switch ((c = *src++)) {
case '\0': /* special, trailing '\' */
c = '\\';
break;
case 'b': /* backspace */
c = '\b';
break;
case 'f': /* form feed */
c = '\f';
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
}
/* default, use the char literally */
}
*dst = '\0';
return ret;
}
int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap)
{
int result;
if (!buffer || !*buffer || !space || !*space)
return -1;
result = vsnprintf(*buffer, *space, fmt, ap);
if (result < 0)
return -1;
else if (result > *space)
result = *space;
*buffer += result;
*space -= result;
return 0;
}
int ast_build_string(char **buffer, size_t *space, const char *fmt, ...)
{
va_list ap;
int result;
va_start(ap, fmt);
result = ast_build_string_va(buffer, space, fmt, ap);
va_end(ap);
return result;
}
int ast_regex_string_to_regex_pattern(const char *regex_string, struct ast_str **regex_pattern)
{
int regex_len = strlen(regex_string);
int ret = 3;
/* Chop off the leading / if there is one */
if ((regex_len >= 1) && (regex_string[0] == '/')) {
ast_str_set(regex_pattern, 0, "%s", regex_string + 1);
ret -= 2;
}
/* Chop off the ending / if there is one */
if ((regex_len > 1) && (regex_string[regex_len - 1] == '/')) {
ast_str_truncate(*regex_pattern, -1);
ret -= 1;
}
return ret;
}
int ast_true(const char *s)
{
if (ast_strlen_zero(s))
return 0;
/* Determine if this is a true value */
if (!strcasecmp(s, "yes") ||
!strcasecmp(s, "true") ||
!strcasecmp(s, "y") ||
!strcasecmp(s, "t") ||
!strcasecmp(s, "1") ||
!strcasecmp(s, "on"))
return -1;
return 0;
}
int ast_false(const char *s)
{
if (ast_strlen_zero(s))
return 0;
/* Determine if this is a false value */
if (!strcasecmp(s, "no") ||
!strcasecmp(s, "false") ||
!strcasecmp(s, "n") ||
!strcasecmp(s, "f") ||
!strcasecmp(s, "0") ||
!strcasecmp(s, "off"))
return -1;
return 0;
}
Kevin P. Fleming
committed
#define ONE_MILLION 1000000
/*
* put timeval in a valid range. usec is 0..999999
* negative values are not allowed and truncated.
*/
static struct timeval tvfix(struct timeval a)
{
if (a.tv_usec >= ONE_MILLION) {
ast_log(LOG_WARNING, "warning too large timestamp %ld.%ld\n",
(long)a.tv_sec, (long int) a.tv_usec);
a.tv_sec += a.tv_usec / ONE_MILLION;
Kevin P. Fleming
committed
a.tv_usec %= ONE_MILLION;
} else if (a.tv_usec < 0) {
ast_log(LOG_WARNING, "warning negative timestamp %ld.%ld\n",
(long)a.tv_sec, (long int) a.tv_usec);
Kevin P. Fleming
committed
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
a.tv_usec = 0;
}
return a;
}
struct timeval ast_tvadd(struct timeval a, struct timeval b)
{
/* consistency checks to guarantee usec in 0..999999 */
a = tvfix(a);
b = tvfix(b);
a.tv_sec += b.tv_sec;
a.tv_usec += b.tv_usec;
if (a.tv_usec >= ONE_MILLION) {
a.tv_sec++;
a.tv_usec -= ONE_MILLION;
}
return a;
}
struct timeval ast_tvsub(struct timeval a, struct timeval b)
{
/* consistency checks to guarantee usec in 0..999999 */
a = tvfix(a);
b = tvfix(b);
a.tv_sec -= b.tv_sec;
a.tv_usec -= b.tv_usec;
if (a.tv_usec < 0) {
a.tv_sec-- ;
a.tv_usec += ONE_MILLION;
}
return a;
}
int ast_remaining_ms(struct timeval start, int max_ms)
{
int ms;
if (max_ms < 0) {
ms = max_ms;
} else {
ms = max_ms - ast_tvdiff_ms(ast_tvnow(), start);
if (ms < 0) {
ms = 0;
}
}
return ms;
}
void ast_format_duration_hh_mm_ss(int duration, char *buf, size_t length)
{
int durh, durm, durs;
durh = duration / 3600;
durm = (duration % 3600) / 60;
durs = duration % 60;
snprintf(buf, length, "%02d:%02d:%02d", durh, durm, durs);
}
Kevin P. Fleming
committed
#undef ONE_MILLION
Joshua Colp
committed
#ifndef linux
Joshua Colp
committed
#endif
long int ast_random(void)
{
long int res;
Joshua Colp
committed
if (dev_urandom_fd >= 0) {
int read_res = read(dev_urandom_fd, &res, sizeof(res));
Joshua Colp
committed
if (read_res > 0) {
Joshua Colp
committed
long int rm = RAND_MAX;
Joshua Colp
committed
res = res < 0 ? ~res : res;
Joshua Colp
committed
rm++;
return res % rm;
Joshua Colp
committed
}
Joshua Colp
committed
}
/* XXX - Thread safety really depends on the libc, not the OS.
*
* But... popular Linux libc's (uClibc, glibc, eglibc), all have a
* somewhat thread safe random(3) (results are random, but not
* reproducible). The libc's for other systems (BSD, et al.), not so
* much.
*/
Joshua Colp
committed
#ifdef linux
res = random();
#else
ast_mutex_lock(&randomlock);
res = random();
ast_mutex_unlock(&randomlock);
Joshua Colp
committed
#endif
void ast_replace_subargument_delimiter(char *s)
{
for (; *s; s++) {
if (*s == '^') {
*s = ',';
}
}
}
Russell Bryant
committed
char *ast_process_quotes_and_slashes(char *start, char find, char replace_with)
{
Russell Bryant
committed
int inEscape = 0;
int inQuotes = 0;
for (; *start; start++) {
if (inEscape) {
*dataPut++ = *start; /* Always goes verbatim */
inEscape = 0;
Russell Bryant
committed
if (*start == '\\') {
inEscape = 1; /* Do not copy \ into the data */
} else if (*start == '\'') {
inQuotes = 1 - inQuotes; /* Do not copy ' into the data */
Russell Bryant
committed
} else {
/* Replace , with |, unless in quotes */
*dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start);
Russell Bryant
committed
}
}
}
if (start != dataPut)
*dataPut = 0;
return dataPut;
}
Russell Bryant
committed
void ast_join_delim(char *s, size_t len, const char * const w[], unsigned int size, char delim)
Russell Bryant
committed
{
int x, ofs = 0;
const char *src;
/* Join words into a string */
if (!s)
return;
for (x = 0; ofs < len && x < size && w[x] ; x++) {
Russell Bryant
committed
if (x > 0)
Russell Bryant
committed
for (src = w[x]; *src && ofs < len; src++)
s[ofs++] = *src;
}
if (ofs == len)
ofs--;
s[ofs] = '\0';
}
char *ast_to_camel_case_delim(const char *s, const char *delim)
{
char *res = ast_strdup(s);
char *front, *back, *buf = res;
int size;
front = strtok_r(buf, delim, &back);
while (front) {
size = strlen(front);
*front = toupper(*front);
ast_copy_string(buf, front, size + 1);
buf += size;
front = strtok_r(NULL, delim, &back);
}
return res;
}
/*
* stringfields support routines.
*/
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
/* this is a little complex... string fields are stored with their
allocated size in the bytes preceding the string; even the
constant 'empty' string has to be this way, so the code that
checks to see if there is enough room for a new string doesn't
have to have any special case checks
*/
static const struct {
ast_string_field_allocation allocation;
char string[1];
} __ast_string_field_empty_buffer;
ast_string_field __ast_string_field_empty = __ast_string_field_empty_buffer.string;
#define ALLOCATOR_OVERHEAD 48
static size_t optimal_alloc_size(size_t size)
{
unsigned int count;
size += ALLOCATOR_OVERHEAD;
for (count = 1; size; size >>= 1, count++);
return (1 << count) - ALLOCATOR_OVERHEAD;
}
/*! \brief add a new block to the pool.
* We can only allocate from the topmost pool, so the
* fields in *mgr reflect the size of that only.
*/
Kevin P. Fleming
committed
static int add_string_pool(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
size_t size, const char *file, int lineno, const char *func)
{
struct ast_string_field_pool *pool;
size_t alloc_size = optimal_alloc_size(sizeof(*pool) + size);
Kevin P. Fleming
committed
#if defined(__AST_DEBUG_MALLOC)
if (!(pool = __ast_calloc(1, alloc_size, file, lineno, func))) {
return -1;
}
#else
if (!(pool = ast_calloc(1, alloc_size))) {
Kevin P. Fleming
committed
#endif
pool->prev = *pool_head;
pool->size = alloc_size - sizeof(*pool);
*pool_head = pool;
/*
* This is an internal API, code should not use it directly.
* It initializes all fields as empty, then uses 'size' for 3 functions:
* size > 0 means initialize the pool list with a pool of given size.
* This must be called right after allocating the object.
* size = 0 means release all pools except the most recent one.
* If the first pool was allocated via embedding in another
* object, that pool will be preserved instead.
* This is useful to e.g. reset an object to the initial value.
* size < 0 means release all pools.
* This must be done before destroying the object.
*/
Kevin P. Fleming
committed
int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
int needed, const char *file, int lineno, const char *func)
const char **p = (const char **) pool_head + 1;
Kevin P. Fleming
committed
struct ast_string_field_pool *cur = NULL;
struct ast_string_field_pool *preserve = NULL;
/* clear fields - this is always necessary */
while ((struct ast_string_field_mgr *) p != mgr) {
*p++ = __ast_string_field_empty;
Kevin P. Fleming
committed
#if defined(__AST_DEBUG_MALLOC)
mgr->owner_file = file;
mgr->owner_func = func;
mgr->owner_line = lineno;
#endif
if (needed > 0) { /* allocate the initial pool */
*pool_head = NULL;
Kevin P. Fleming
committed
return add_string_pool(mgr, pool_head, needed, file, lineno, func);
Kevin P. Fleming
committed
/* if there is an embedded pool, we can't actually release *all*
* pools, we must keep the embedded one. if the caller is about
* to free the structure that contains the stringfield manager
* and embedded pool anyway, it will be freed as part of that
* operation.
*/
if ((needed < 0) && mgr->embedded_pool) {
needed = 0;
}
Kevin P. Fleming
committed
if (needed < 0) { /* reset all pools */
cur = *pool_head;
} else if (mgr->embedded_pool) { /* preserve the embedded pool */
preserve = mgr->embedded_pool;
cur = *pool_head;
Kevin P. Fleming
committed
} else { /* preserve the last pool */
if (*pool_head == NULL) {