Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:48:22 2009

## Asterisk developer's documentation

# astobj2.c File Reference

`#include "asterisk.h"`
`#include <stdlib.h>`
`#include "asterisk/astobj2.h"`
`#include "asterisk/utils.h"`
`#include "asterisk/cli.h"`
`#include "asterisk/linkedlists.h"`

Include dependency graph for astobj2.c:

Go to the source code of this file.

## Data Structures

struct | __priv_data
struct | ao2_container
struct | astobj2
struct | bucket_list

## Defines

#define | AO2_MAGIC 0xa570b123
#define | EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
| convert from a pointer _p to an astobj2 object

## Functions

void * | __ao2_link (struct ao2_container *c, void *user_data, int iax2_hack)
void * | ao2_alloc (size_t data_size, ao2_destructor_fn destructor_fn)
void | ao2_bt (void)
void * | ao2_callback (struct ao2_container *c, const enum search_flags flags, ao2_callback_fn cb_fn, void *arg)
struct ao2_container * | ao2_container_alloc (const unsigned int n_buckets, ao2_hash_fn hash_fn, ao2_callback_fn cmp_fn)
int | ao2_container_count (struct ao2_container *c)
void * | ao2_find (struct ao2_container *c, void *arg, enum search_flags flags)
struct ao2_iterator | ao2_iterator_init (struct ao2_container *c, int flags)
void * | ao2_iterator_next (struct ao2_iterator *a)
int | ao2_lock (void *user_data)
int | ao2_match_by_addr (void *user_data, void *arg, int flags)
| another convenience function is a callback that matches on address
int | ao2_ref (void *user_data, const int delta)
void * | ao2_unlink (struct ao2_container *c, void *user_data)
int | ao2_unlock (void *user_data)
| AST_LIST_HEAD_NOLOCK (bucket, bucket_list)
int | astobj2_init (void)
static int | cb_true (void *user_data, void *arg, int flags)
| special callback that matches all
static int | cd_cb (void *obj, void *arg, int flag)
static void | container_destruct (void *c)
static int | hash_zero (const void *user_obj, const int flags)
| always zero hash function
static struct astobj2 * | INTERNAL_OBJ (void *user_data)
| convert from a pointer _p to a user-defined object

## Define Documentation

#define AO2_MAGIC 0xa570b123

Definition at line 52 of file astobj2.c.

Referenced by ao2_alloc(), and INTERNAL_OBJ().

#define EXTERNAL_OBJ | ( | _p |  | ) | ((_p) == NULL ? NULL : (_p)->user_data)

convert from a pointer _p to an astobj2 object

Returns:
  the pointer to the user-defined portion.

Definition at line 129 of file astobj2.c.

Referenced by ao2_alloc(), ao2_callback(), and ao2_iterator_next().

## Function Documentation

void* __ao2_link | ( | struct ao2_container * | c,
|  | void * | user_data,
|  | int | iax2_hack |
| ) |  |  |

Definition at line 343 of file astobj2.c.

References ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_INSERT_HEAD, AST_LIST_INSERT_TAIL, ao2_container::buckets, calloc, ao2_container::elements, ao2_container::hash_fn, INTERNAL_OBJ(), ao2_container::n_buckets, and ao2_container::version.

    00344 {
    00345    int i;
    00346    /* create a new list entry */
    00347    struct bucket_list *p;
    00348    struct astobj2 *obj = INTERNAL_OBJ(user_data);
    00349
    00350    if (!obj)
    00351       return NULL;
    00352
    00353    if (INTERNAL_OBJ(c) == NULL)
    00354       return NULL;
    00355
    00356    p = calloc(1, sizeof(*p));
    00357    if (!p)
    00358       return NULL;
    00359
    00360    i = c->hash_fn(user_data, OBJ_POINTER);
    00361
    00362    ao2_lock(c);
    00363    i %= c->n_buckets;
    00364    p->astobj = obj;
    00365    p->version = ast_atomic_fetchadd_int(&c->version, 1);
    00366    if (iax2_hack)
    00367       AST_LIST_INSERT_HEAD(&c->buckets[i], p, entry);
    00368    else
    00369       AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
    00370    ast_atomic_fetchadd_int(&c->elements, 1);
    00371    ao2_ref(user_data, +1);
    00372    ao2_unlock(c);
    00373
    00374    return p;
    00375 }

void* ao2_alloc | ( | size_t | data_size,
|  | ao2_destructor_fn | destructor_fn |
| ) |  |  |

Definition at line 210 of file astobj2.c.

References AO2_MAGIC, ast_mutex_init(), calloc, __priv_data::data_size, __priv_data::destructor_fn, EXTERNAL_OBJ, __priv_data::lock, __priv_data::magic, astobj2::priv_data, and __priv_data::ref_counter.

Referenced by add_calltoken_ignore(), ao2_container_alloc(), build_callno_limits(), create_callno_pools(), new_iax(), and peercnt_add().

    00211 {
    00212    /* allocation */
    00213    struct astobj2 *obj;
    00214
    00215    if (data_size < sizeof(void *))
    00216       data_size = sizeof(void *);
    00217
    00218    obj = calloc(1, sizeof(*obj) + data_size);
    00219
    00220    if (obj == NULL)
    00221       return NULL;
    00222
    00223    ast_mutex_init(&obj->priv_data.lock);
    00224    obj->priv_data.magic = AO2_MAGIC;
    00225    obj->priv_data.data_size = data_size;
    00226    obj->priv_data.ref_counter = 1;
    00227    obj->priv_data.destructor_fn = destructor_fn;   /* can be NULL */
    00228
    00229 #ifdef AO2_DEBUG
    00230    ast_atomic_fetchadd_int(&ao2.total_objects, 1);
    00231    ast_atomic_fetchadd_int(&ao2.total_mem, data_size);
    00232    ast_atomic_fetchadd_int(&ao2.total_refs, 1);
    00233 #endif
    00234
    00235    /* return a pointer to the user data */
    00236    return EXTERNAL_OBJ(obj);
    00237 }

void ao2_bt | ( | void |  | ) |

Definition at line 80 of file astobj2.c.

    00080 {}

void* ao2_callback | ( | struct ao2_container * | c,
|  | const enum search_flags | flags,
|  | ao2_callback_fn | cb_fn,
|  | void * | arg |
| ) |  |  |

Browse the container using different stategies accoding the flags.

Returns:
  Is a pointer to an object or to a list of object if OBJ_MULTIPLE is specified.

Definition at line 412 of file astobj2.c.

References ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_log(), ao2_container::buckets, cb_true(), ao2_container::elements, EXTERNAL_OBJ, free, ao2_container::hash_fn, INTERNAL_OBJ(), last, LOG_WARNING, match(), ao2_container::n_buckets, and ao2_container::version.

Referenced by ao2_find(), ao2_unlink(), calltoken_required(), container_destruct(), and set_peercnt_limit().

    00415 {
    00416    int i, start, last;  /* search boundaries */
    00417    void *ret = NULL;
    00418
    00419    if (INTERNAL_OBJ(c) == NULL)  /* safety check on the argument */
    00420       return NULL;
    00421
    00422    if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
    00423       ast_log(LOG_WARNING, "multiple data return not implemented yet (flags %x)\n", flags);
    00424       return NULL;
    00425    }
    00426
    00427    /* override the match function if necessary */
    00428 #if 0
    00429    /* Removing this slightly changes the meaning of OBJ_POINTER, but makes it
    00430     * do what I want it to.  I'd like to hint to ao2_callback that the arg is
    00431     * of the same object type, so it can be passed to the hash function.
    00432     * However, I don't want to imply that this is the object being searched for. */
    00433    if (flags & OBJ_POINTER)
    00434       cb_fn = match_by_addr;
    00435    else
    00436 #endif
    00437    if (cb_fn == NULL)   /* if NULL, match everything */
    00438       cb_fn = cb_true;
    00439    /*
    00440     * XXX this can be optimized.
    00441     * If we have a hash function and lookup by pointer,
    00442     * run the hash function. Otherwise, scan the whole container
    00443     * (this only for the time being. We need to optimize this.)
    00444     */
    00445    if ((flags & OBJ_POINTER)) /* we know hash can handle this case */
    00446       start = i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
    00447    else        /* don't know, let's scan all buckets */
    00448       i = -1;     /* XXX this must be fixed later. */
    00449
    00450    /* determine the search boundaries: i..last-1 */
    00451    if (i < 0) {
    00452       start = i = 0;
    00453       last = c->n_buckets;
    00454    } else if ((flags & OBJ_CONTINUE)) {
    00455       last = c->n_buckets;
    00456    } else {
    00457       last = i + 1;
    00458    }
    00459
    00460    ao2_lock(c);   /* avoid modifications to the content */
    00461
    00462    for (; i < last ; i++) {
    00463       /* scan the list with prev-cur pointers */
    00464       struct bucket_list *cur;
    00465
    00466       AST_LIST_TRAVERSE_SAFE_BEGIN(&c->buckets[i], cur, entry) {
    00467          int match = cb_fn(EXTERNAL_OBJ(cur->astobj), arg, flags) & (CMP_MATCH | CMP_STOP);
    00468
    00469          /* we found the object, performing operations according flags */
    00470          if (match == 0) { /* no match, no stop, continue */
    00471             continue;
    00472          } else if (match == CMP_STOP) {  /* no match but stop, we are done */
    00473             i = last;
    00474             break;
    00475          }
    00476          /* we have a match (CMP_MATCH) here */
    00477          if (!(flags & OBJ_NODATA)) {  /* if must return the object, record the value */
    00478             /* it is important to handle this case before the unlink */
    00479             ret = EXTERNAL_OBJ(cur->astobj);
    00480             ao2_ref(ret, 1);
    00481          }
    00482
    00483          if (flags & OBJ_UNLINK) {  /* must unlink */
    00484             struct bucket_list *x = cur;
    00485
    00486             /* we are going to modify the container, so update version */
    00487             ast_atomic_fetchadd_int(&c->version, 1);
    00488             AST_LIST_REMOVE_CURRENT(&c->buckets[i], entry);
    00489             /* update number of elements and version */
    00490             ast_atomic_fetchadd_int(&c->elements, -1);
    00491             ao2_ref(EXTERNAL_OBJ(x->astobj), -1);
    00492             free(x); /* free the link record */
    00493          }
    00494
    00495          if ((match & CMP_STOP) || (flags & OBJ_MULTIPLE) == 0) {
    00496             /* We found the only match we need */
    00497             i = last;   /* force exit from outer loop */
    00498             break;
    00499          }
    00500          if (!(flags & OBJ_NODATA)) {
    00501 #if 0 /* XXX to be completed */
    00502             /*
    00503              * This is the multiple-return case. We need to link
    00504              * the object in a list. The refcount is already increased.
    00505              */
    00506 #endif
    00507          }
    00508       }
    00509       AST_LIST_TRAVERSE_SAFE_END
    00510
    00511       if (ret) {
    00512          /* This assumes OBJ_MULTIPLE with !OBJ_NODATA is still not implemented */
    00513          break;
    00514       }
    00515
    00516       if (i == c->n_buckets - 1 && (flags & OBJ_POINTER) && (flags & OBJ_CONTINUE)) {
    00517          /* Move to the beginning to ensure we check every bucket */
    00518          i = -1;
    00519          last = start;
    00520       }
    00521    }
    00522    ao2_unlock(c);
    00523    return ret;
    00524 }

struct ao2_container* ao2_container_alloc | ( | const unsigned int | n_buckets,
|  | ao2_hash_fn | hash_fn,
|  | ao2_callback_fn | cmp_fn |
| ) |  |  | `[read]`

Definition at line 297 of file astobj2.c.

References ao2_alloc(), ao2_container::cmp_fn, container_destruct(), ao2_container::hash_fn, hash_zero(), ao2_container::n_buckets, and ao2_container::version.

Referenced by create_callno_pools(), and load_objects().

    00299 {
    00300    /* XXX maybe consistency check on arguments ? */
    00301    /* compute the container size */
    00302    size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
    00303
    00304    struct ao2_container *c = ao2_alloc(container_size, container_destruct);
    00305
    00306    if (!c)
    00307       return NULL;
    00308
    00309    c->version = 1;   /* 0 is a reserved value here */
    00310    c->n_buckets = n_buckets;
    00311    c->hash_fn = hash_fn ? hash_fn : hash_zero;
    00312    c->cmp_fn = cmp_fn;
    00313
    00314 #ifdef AO2_DEBUG
    00315    ast_atomic_fetchadd_int(&ao2.total_containers, 1);
    00316 #endif
    00317
    00318    return c;
    00319 }

int ao2_container_count | ( | struct ao2_container * | c | ) |

return the number of elements in the container

Definition at line 324 of file astobj2.c.

References ao2_container::elements.

Referenced by get_unused_callno().

    00325 {
    00326    return c->elements;
    00327 }

void* ao2_find | ( | struct ao2_container * | c,
|  | void * | arg,
|  | enum search_flags | flags |
| ) |  |  |

the find function just invokes the default callback with some reasonable flags.

Definition at line 529 of file astobj2.c.

References ao2_callback(), and ao2_container::cmp_fn.

Referenced by add_calltoken_ignore(), build_callno_limits(), find_callno(), get_unused_callno(), peercnt_add(), peercnt_modify(), peercnt_remove_by_addr(), and sched_delay_remove().

    00530 {
    00531    return ao2_callback(c, flags, c->cmp_fn, arg);
    00532 }

struct ao2_iterator ao2_iterator_init | ( | struct ao2_container * | c,
|  | int | flags |
| ) |  |  | `[read]`

initialize an iterator so we start from the first object

Definition at line 537 of file astobj2.c.

References ao2_iterator::c, and ao2_iterator::flags.

Referenced by iax2_show_callnumber_usage().

    00538 {
    00539    struct ao2_iterator a = {
    00540       .c = c,
    00541       .flags = flags
    00542    };
    00543
    00544    return a;
    00545 }

void* ao2_iterator_next | ( | struct ao2_iterator * | a | ) |

Definition at line 550 of file astobj2.c.

References ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_NEXT, AST_LIST_TRAVERSE, ao2_iterator::bucket, ao2_container::buckets, ao2_iterator::c, ao2_iterator::c_version, EXTERNAL_OBJ, F_AO2I_DONTLOCK, ao2_iterator::flags, INTERNAL_OBJ(), ao2_container::n_buckets, ao2_iterator::obj, ao2_iterator::version, and ao2_container::version.

Referenced by iax2_show_callnumber_usage().

    00551 {
    00552    int lim;
    00553    struct bucket_list *p = NULL;
    00554    void *ret = NULL;
    00555
    00556    if (INTERNAL_OBJ(a->c) == NULL)
    00557       return NULL;
    00558
    00559    if (!(a->flags & F_AO2I_DONTLOCK))
    00560       ao2_lock(a->c);
    00561
    00562    /* optimization. If the container is unchanged and
    00563     * we have a pointer, try follow it
    00564     */
    00565    if (a->c->version == a->c_version && (p = a->obj) ) {
    00566       if ( (p = AST_LIST_NEXT(p, entry)) )
    00567          goto found;
    00568       /* nope, start from the next bucket */
    00569       a->bucket++;
    00570       a->version = 0;
    00571       a->obj = NULL;
    00572    }
    00573
    00574    lim = a->c->n_buckets;
    00575
    00576    /* Browse the buckets array, moving to the next
    00577     * buckets if we don't find the entry in the current one.
    00578     * Stop when we find an element with version number greater
    00579     * than the current one (we reset the version to 0 when we
    00580     * switch buckets).
    00581     */
    00582    for (; a->bucket < lim; a->bucket++, a->version = 0) {
    00583       /* scan the current bucket */
    00584       AST_LIST_TRAVERSE(&a->c->buckets[a->bucket], p, entry) {
    00585          if (p->version > a->version)
    00586             goto found;
    00587       }
    00588    }
    00589
    00590 found:
    00591    if (p) {
    00592       a->version = p->version;
    00593       a->obj = p;
    00594       a->c_version = a->c->version;
    00595       ret = EXTERNAL_OBJ(p->astobj);
    00596       /* inc refcount of returned object */
    00597       ao2_ref(ret, 1);
    00598    }
    00599
    00600    if (!(a->flags & F_AO2I_DONTLOCK))
    00601       ao2_unlock(a->c);
    00602
    00603    return ret;
    00604 }

int ao2_lock | ( | void * | user_data | ) |

Definition at line 131 of file astobj2.c.

References ast_mutex_lock(), INTERNAL_OBJ(), __priv_data::lock, and astobj2::priv_data.

Referenced by __ao2_link(), add_calltoken_ignore(), ao2_callback(), ao2_iterator_next(), build_callno_limits(), get_unused_callno(), peercnt_add(), peercnt_remove(), and replace_callno().

    00132 {
    00133    struct astobj2 *p = INTERNAL_OBJ(user_data);
    00134
    00135    if (p == NULL)
    00136       return -1;
    00137
    00138 #ifdef AO2_DEBUG
    00139    ast_atomic_fetchadd_int(&ao2.total_locked, 1);
    00140 #endif
    00141
    00142    return ast_mutex_lock(&p->priv_data.lock);
    00143 }

int ao2_match_by_addr | ( | void * | user_data,
|  | void * | arg,
|  | int | flags |
| ) |  |  |

another convenience function is a callback that matches on address

Definition at line 380 of file astobj2.c.

Referenced by ao2_unlink().

    00381 {
    00382    return (user_data == arg) ? (CMP_MATCH | CMP_STOP) : 0;
    00383 }

int ao2_ref | ( | void * | user_data,
|  | const int | delta |
| ) |  |  |

Definition at line 162 of file astobj2.c.

References ast_log(), ast_mutex_destroy(), __priv_data::data_size, __priv_data::destructor_fn, free, INTERNAL_OBJ(), __priv_data::lock, LOG_ERROR, astobj2::priv_data, and __priv_data::ref_counter.

Referenced by __ao2_link(), __unload_module(), add_calltoken_ignore(), ao2_callback(), ao2_iterator_next(), build_callno_limits(), calltoken_required(), cd_cb(), create_callno_pools(), find_callno(), iax2_destroy(), iax2_show_callnumber_usage(), load_objects(), peercnt_add(), peercnt_modify(), peercnt_remove_by_addr(), peercnt_remove_cb(), replace_callno(), sched_delay_remove(), and set_peercnt_limit().

    00163 {
    00164    int current_value;
    00165    int ret;
    00166    struct astobj2 *obj = INTERNAL_OBJ(user_data);
    00167
    00168    if (obj == NULL)
    00169       return -1;
    00170
    00171    /* if delta is 0, just return the refcount */
    00172    if (delta == 0)
    00173       return (obj->priv_data.ref_counter);
    00174
    00175    /* we modify with an atomic operation the reference counter */
    00176    ret = ast_atomic_fetchadd_int(&obj->priv_data.ref_counter, delta);
    00177    current_value = ret + delta;
    00178
    00179 #ifdef AO2_DEBUG
    00180    ast_atomic_fetchadd_int(&ao2.total_refs, delta);
    00181 #endif
    00182
    00183    /* this case must never happen */
    00184    if (current_value < 0)
    00185       ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, user_data);
    00186
    00187    if (current_value <= 0) { /* last reference, destroy the object */
    00188       if (obj->priv_data.destructor_fn != NULL)
    00189          obj->priv_data.destructor_fn(user_data);
    00190
    00191       ast_mutex_destroy(&obj->priv_data.lock);
    00192 #ifdef AO2_DEBUG
    00193       ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
    00194       ast_atomic_fetchadd_int(&ao2.total_objects, -1);
    00195 #endif
    00196       /* for safety, zero-out the astobj2 header and also the
    00197        * first word of the user-data, which we make sure is always
    00198        * allocated. */
    00199       bzero(obj, sizeof(struct astobj2 *) + sizeof(void *) );
    00200       free(obj);
    00201    }
    00202
    00203    return ret;
    00204 }

void* ao2_unlink | ( | struct ao2_container * | c,
|  | void * | user_data |
| ) |  |  |

Definition at line 389 of file astobj2.c.

References ao2_callback(), ao2_match_by_addr(), and INTERNAL_OBJ().

Referenced by peercnt_remove(), and remove_by_peercallno().

    00390 {
    00391    if (INTERNAL_OBJ(user_data) == NULL)   /* safety check on the argument */
    00392       return NULL;
    00393
    00394    ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data);
    00395
    00396    return NULL;
    00397 }

int ao2_unlock | ( | void * | user_data | ) |

Definition at line 145 of file astobj2.c.

References ast_mutex_unlock(), INTERNAL_OBJ(), __priv_data::lock, and astobj2::priv_data.

Referenced by __ao2_link(), add_calltoken_ignore(), ao2_callback(), ao2_iterator_next(), build_callno_limits(), get_unused_callno(), peercnt_add(), peercnt_remove(), and replace_callno().

    00146 {
    00147    struct astobj2 *p = INTERNAL_OBJ(user_data);
    00148
    00149    if (p == NULL)
    00150       return -1;
    00151
    00152 #ifdef AO2_DEBUG
    00153    ast_atomic_fetchadd_int(&ao2.total_locked, -1);
    00154 #endif
    00155
    00156    return ast_mutex_unlock(&p->priv_data.lock);
    00157 }

AST_LIST_HEAD_NOLOCK | ( | bucket | ,
|  | bucket_list |  |
| ) |  |  |

int astobj2_init | ( | void |  | ) |

Definition at line 728 of file astobj2.c.

References ast_cli_register_multiple().

    00729 {
    00730 #ifdef AO2_DEBUG
    00731    ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
    00732 #endif
    00733
    00734    return 0;
    00735 }

static int cb_true | ( | void * | user_data,
|  | void * | arg,
|  | int | flags |
| ) |  |  | `[static]`

special callback that matches all

Definition at line 402 of file astobj2.c.

Referenced by ao2_callback().

    00403 {
    00404    return CMP_MATCH;
    00405 }

static int cd_cb | ( | void * | obj,
|  | void * | arg,
|  | int | flag |
| ) |  |  | `[static]`

Definition at line 609 of file astobj2.c.

References ao2_ref().

Referenced by container_destruct().

    00610 {
    00611    ao2_ref(obj, -1);
    00612    return 0;
    00613 }

static void container_destruct | ( | void * | c | ) | `[static]`

Definition at line 615 of file astobj2.c.

References ao2_callback(), and cd_cb().

Referenced by ao2_container_alloc().

    00616 {
    00617    struct ao2_container *c = _c;
    00618
    00619    ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
    00620
    00621 #ifdef AO2_DEBUG
    00622    ast_atomic_fetchadd_int(&ao2.total_containers, -1);
    00623 #endif
    00624 }

static int hash_zero | ( | const void * | user_obj,
|  | const int | flags |
| ) |  |  | `[static]`

always zero hash function

it is convenient to have a hash function that always returns 0. This is basically used when we want to have a container that is a simple linked list.

Returns:
  0

Definition at line 288 of file astobj2.c.

Referenced by ao2_container_alloc().

    00289 {
    00290    return 0;
    00291 }

static struct astobj2* INTERNAL_OBJ | ( | void * | user_data | ) | `[static, read]`

convert from a pointer _p to a user-defined object

Returns:
  the pointer to the astobj2 structure

Definition at line 106 of file astobj2.c.

References AO2_MAGIC, ast_log(), LOG_ERROR, __priv_data::magic, and astobj2::priv_data.

Referenced by __ao2_link(), ao2_callback(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlink(), and ao2_unlock().

    00107 {
    00108    struct astobj2 *p;
    00109
    00110    if (!user_data) {
    00111       ast_log(LOG_ERROR, "user_data is NULL\n");
    00112       return NULL;
    00113    }
    00114
    00115    p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
    00116    if (AO2_MAGIC != (p->priv_data.magic) ) {
    00117       ast_log(LOG_ERROR, "bad magic number 0x%x for %p\n", p->priv_data.magic, p);
    00118       p = NULL;
    00119    }
    00120
    00121    return p;
    00122 }