diff options
author | Benny Prijono <bennylp@teluu.com> | 2013-02-21 11:18:36 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2013-02-21 11:18:36 +0000 |
commit | 9b8e0a5afe9cba0fd430e9642630bd465db9aefa (patch) | |
tree | 550cd346c99b144a79c76096dc75d6be2dfb3c31 /pjlib/src/pj/lock.c | |
parent | 99de12689e7c6b8117ee82ff8e3c17e5ec85418d (diff) |
Fixed #1616: Implementation of Group lock and other foundation in PJLIB for fixing synchronization issues
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4359 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjlib/src/pj/lock.c')
-rw-r--r-- | pjlib/src/pj/lock.c | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/pjlib/src/pj/lock.c b/pjlib/src/pj/lock.c index c281f8b3..04b1b670 100644 --- a/pjlib/src/pj/lock.c +++ b/pjlib/src/pj/lock.c @@ -20,10 +20,12 @@ #include <pj/lock.h> #include <pj/os.h> #include <pj/assert.h> +#include <pj/log.h> #include <pj/pool.h> #include <pj/string.h> #include <pj/errno.h> +#define THIS_FILE "lock.c" typedef void LOCK_OBJ; @@ -196,3 +198,516 @@ PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock ) return (*lock->destroy)(lock->lock_object); } + +/****************************************************************************** + * Group lock + */ + +/* Individual lock in the group lock */ +typedef struct grp_lock_item +{ + PJ_DECL_LIST_MEMBER(struct grp_lock_item); + int prio; + pj_lock_t *lock; + +} grp_lock_item; + +/* Destroy callbacks */ +typedef struct grp_destroy_callback +{ + PJ_DECL_LIST_MEMBER(struct grp_destroy_callback); + void *comp; + void (*handler)(void*); +} grp_destroy_callback; + +#if PJ_GRP_LOCK_DEBUG +/* Store each add_ref caller */ +typedef struct grp_lock_ref +{ + PJ_DECL_LIST_MEMBER(struct grp_lock_ref); + const char *file; + int line; +} grp_lock_ref; +#endif + +/* The group lock */ +struct pj_grp_lock_t +{ + pj_lock_t base; + + pj_pool_t *pool; + pj_atomic_t *ref_cnt; + pj_lock_t *own_lock; + + pj_thread_t *owner; + int owner_cnt; + + grp_lock_item lock_list; + grp_destroy_callback destroy_list; + +#if PJ_GRP_LOCK_DEBUG + grp_lock_ref ref_list; + grp_lock_ref ref_free_list; +#endif +}; + + +PJ_DEF(void) pj_grp_lock_config_default(pj_grp_lock_config *cfg) +{ + pj_bzero(cfg, sizeof(*cfg)); +} + +static void grp_lock_set_owner_thread(pj_grp_lock_t *glock) +{ + if (!glock->owner) { + glock->owner = pj_thread_this(); + glock->owner_cnt = 1; + } else { + pj_assert(glock->owner == pj_thread_this()); + glock->owner_cnt++; + } +} + +static void grp_lock_unset_owner_thread(pj_grp_lock_t *glock) +{ + pj_assert(glock->owner == pj_thread_this()); + pj_assert(glock->owner_cnt > 0); + if (--glock->owner_cnt <= 0) { + glock->owner = NULL; + glock->owner_cnt = 0; + } +} + +static pj_status_t grp_lock_acquire(LOCK_OBJ *p) +{ + pj_grp_lock_t *glock = (pj_grp_lock_t*)p; + grp_lock_item *lck; + + pj_assert(pj_atomic_get(glock->ref_cnt) > 0); + + lck = glock->lock_list.next; + while (lck != &glock->lock_list) { + pj_lock_acquire(lck->lock); + lck = lck->next; + } + grp_lock_set_owner_thread(glock); + pj_grp_lock_add_ref(glock); + return PJ_SUCCESS; +} + +static pj_status_t grp_lock_tryacquire(LOCK_OBJ *p) +{ + pj_grp_lock_t *glock = (pj_grp_lock_t*)p; + grp_lock_item *lck; + + pj_assert(pj_atomic_get(glock->ref_cnt) > 0); + + lck = glock->lock_list.next; + while (lck != &glock->lock_list) { + pj_status_t status = pj_lock_tryacquire(lck->lock); + if (status != PJ_SUCCESS) { + lck = lck->prev; + while (lck != &glock->lock_list) { + pj_lock_release(lck->lock); + lck = lck->prev; + } + return status; + } + lck = lck->next; + } + grp_lock_set_owner_thread(glock); + pj_grp_lock_add_ref(glock); + return PJ_SUCCESS; +} + +static pj_status_t grp_lock_release(LOCK_OBJ *p) +{ + pj_grp_lock_t *glock = (pj_grp_lock_t*)p; + grp_lock_item *lck; + + grp_lock_unset_owner_thread(glock); + + lck = glock->lock_list.prev; + while (lck != &glock->lock_list) { + pj_lock_release(lck->lock); + lck = lck->prev; + } + return pj_grp_lock_dec_ref(glock); +} + +static pj_status_t grp_lock_destroy(LOCK_OBJ *p) +{ + pj_grp_lock_t *glock = (pj_grp_lock_t*)p; + pj_pool_t *pool = glock->pool; + grp_lock_item *lck; + grp_destroy_callback *cb; + + if (!glock->pool) { + /* already destroyed?! */ + return PJ_EINVAL; + } + + /* Release all chained locks */ + lck = glock->lock_list.next; + while (lck != &glock->lock_list) { + if (lck->lock != glock->own_lock) { + unsigned i; + for (i=0; i<glock->owner_cnt; ++i) + pj_lock_release(lck->lock); + } + lck = lck->next; + } + + /* Call callbacks */ + cb = glock->destroy_list.next; + while (cb != &glock->destroy_list) { + grp_destroy_callback *next = cb->next; + cb->handler(cb->comp); + cb = next; + } + + pj_lock_destroy(glock->own_lock); + pj_atomic_destroy(glock->ref_cnt); + glock->pool = NULL; + pj_pool_release(pool); + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_grp_lock_create( pj_pool_t *pool, + const pj_grp_lock_config *cfg, + pj_grp_lock_t **p_grp_lock) +{ + pj_grp_lock_t *glock; + grp_lock_item *own_lock; + pj_status_t status; + + PJ_ASSERT_RETURN(pool && p_grp_lock, PJ_EINVAL); + + PJ_UNUSED_ARG(cfg); + + pool = pj_pool_create(pool->factory, "glck%p", 512, 512, NULL); + if (!pool) + return PJ_ENOMEM; + + glock = PJ_POOL_ZALLOC_T(pool, pj_grp_lock_t); + glock->base.lock_object = glock; + glock->base.acquire = &grp_lock_acquire; + glock->base.tryacquire = &grp_lock_tryacquire; + glock->base.release = &grp_lock_release; + glock->base.destroy = &grp_lock_destroy; + + glock->pool = pool; + pj_list_init(&glock->lock_list); + pj_list_init(&glock->destroy_list); +#if PJ_GRP_LOCK_DEBUG + pj_list_init(&glock->ref_list); + pj_list_init(&glock->ref_free_list); +#endif + + status = pj_atomic_create(pool, 0, &glock->ref_cnt); + if (status != PJ_SUCCESS) + goto on_error; + + status = pj_lock_create_recursive_mutex(pool, pool->obj_name, + &glock->own_lock); + if (status != PJ_SUCCESS) + goto on_error; + + own_lock = PJ_POOL_ZALLOC_T(pool, grp_lock_item); + own_lock->lock = glock->own_lock; + pj_list_push_back(&glock->lock_list, own_lock); + + *p_grp_lock = glock; + return PJ_SUCCESS; + +on_error: + grp_lock_destroy(glock); + return status; +} + +PJ_DEF(pj_status_t) pj_grp_lock_destroy( pj_grp_lock_t *grp_lock) +{ + return grp_lock_destroy(grp_lock); +} + +PJ_DEF(pj_status_t) pj_grp_lock_acquire( pj_grp_lock_t *grp_lock) +{ + return grp_lock_acquire(grp_lock); +} + +PJ_DEF(pj_status_t) pj_grp_lock_tryacquire( pj_grp_lock_t *grp_lock) +{ + return grp_lock_tryacquire(grp_lock); +} + +PJ_DEF(pj_status_t) pj_grp_lock_release( pj_grp_lock_t *grp_lock) +{ + return grp_lock_release(grp_lock); +} + +PJ_DEF(pj_status_t) pj_grp_lock_replace( pj_grp_lock_t *old, + pj_grp_lock_t *new) +{ + grp_destroy_callback *ocb; + + /* Move handlers from old to new */ + ocb = old->destroy_list.next; + while (ocb != &old->destroy_list) { + grp_destroy_callback *ncb; + + ncb = PJ_POOL_ALLOC_T(new->pool, grp_destroy_callback); + ncb->comp = ocb->comp; + ncb->handler = ocb->handler; + pj_list_push_back(&new->destroy_list, ncb); + + ocb = ocb->next; + } + + pj_list_init(&old->destroy_list); + + grp_lock_destroy(old); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_grp_lock_add_handler( pj_grp_lock_t *glock, + pj_pool_t *pool, + void *comp, + void (*destroy)(void *comp)) +{ + grp_destroy_callback *cb; + + grp_lock_acquire(glock); + + if (pool == NULL) + pool = glock->pool; + + cb = PJ_POOL_ZALLOC_T(pool, grp_destroy_callback); + cb->comp = comp; + cb->handler = destroy; + pj_list_push_back(&glock->destroy_list, cb); + + grp_lock_release(glock); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_grp_lock_del_handler( pj_grp_lock_t *glock, + void *comp, + void (*destroy)(void *comp)) +{ + grp_destroy_callback *cb; + + grp_lock_acquire(glock); + + cb = glock->destroy_list.next; + while (cb != &glock->destroy_list) { + if (cb->comp == comp && cb->handler == destroy) + break; + cb = cb->next; + } + + if (cb != &glock->destroy_list) + pj_list_erase(cb); + + grp_lock_release(glock); + return PJ_SUCCESS; +} + +static pj_status_t grp_lock_add_ref(pj_grp_lock_t *glock) +{ + pj_atomic_inc(glock->ref_cnt); + return PJ_SUCCESS; +} + +static pj_status_t grp_lock_dec_ref(pj_grp_lock_t *glock) +{ + int cnt; /* for debugging */ + if ((cnt=pj_atomic_dec_and_get(glock->ref_cnt)) == 0) { + grp_lock_destroy(glock); + return PJ_EGONE; + } + pj_assert(cnt > 0); + pj_grp_lock_dump(glock); + return PJ_SUCCESS; +} + +#if PJ_GRP_LOCK_DEBUG +PJ_DEF(pj_status_t) pj_grp_lock_add_ref_dbg(pj_grp_lock_t *glock, + const char *file, + int line) +{ + grp_lock_ref *ref; + pj_status_t status; + + pj_enter_critical_section(); + if (!pj_list_empty(&glock->ref_free_list)) { + ref = glock->ref_free_list.next; + pj_list_erase(ref); + } else { + ref = PJ_POOL_ALLOC_T(glock->pool, grp_lock_ref); + } + + ref->file = file; + ref->line = line; + pj_list_push_back(&glock->ref_list, ref); + + pj_leave_critical_section(); + + status = grp_lock_add_ref(glock); + + if (status != PJ_SUCCESS) { + pj_enter_critical_section(); + pj_list_erase(ref); + pj_list_push_back(&glock->ref_free_list, ref); + pj_leave_critical_section(); + } + + return status; +} + +PJ_DEF(pj_status_t) pj_grp_lock_dec_ref_dbg(pj_grp_lock_t *glock, + const char *file, + int line) +{ + grp_lock_ref *ref; + + pj_enter_critical_section(); + /* Find the same source file */ + ref = glock->ref_list.next; + while (ref != &glock->ref_list) { + if (strcmp(ref->file, file) == 0) { + pj_list_erase(ref); + pj_list_push_back(&glock->ref_free_list, ref); + break; + } + ref = ref->next; + } + pj_leave_critical_section(); + + if (ref == &glock->ref_list) { + PJ_LOG(2,(THIS_FILE, "pj_grp_lock_dec_ref_dbg() could not find " + "matching ref for %s", file)); + } + + return grp_lock_dec_ref(glock); +} +#else +PJ_DEF(pj_status_t) pj_grp_lock_add_ref(pj_grp_lock_t *glock) +{ + return grp_lock_add_ref(glock); +} + +PJ_DEF(pj_status_t) pj_grp_lock_dec_ref(pj_grp_lock_t *glock) +{ + return grp_lock_dec_ref(glock); +} +#endif + +PJ_DEF(int) pj_grp_lock_get_ref(pj_grp_lock_t *glock) +{ + return pj_atomic_get(glock->ref_cnt); +} + +PJ_DEF(pj_status_t) pj_grp_lock_chain_lock( pj_grp_lock_t *glock, + pj_lock_t *lock, + int pos) +{ + grp_lock_item *lck, *new_lck; + unsigned i; + + grp_lock_acquire(glock); + + for (i=0; i<glock->owner_cnt; ++i) + pj_lock_acquire(lock); + + lck = glock->lock_list.next; + while (lck != &glock->lock_list) { + if (lck->prio >= pos) + break; + lck = lck->next; + } + + new_lck = PJ_POOL_ZALLOC_T(glock->pool, grp_lock_item); + new_lck->prio = pos; + new_lck->lock = lock; + pj_list_insert_before(lck, new_lck); + + /* this will also release the new lock */ + grp_lock_release(glock); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_grp_lock_unchain_lock( pj_grp_lock_t *glock, + pj_lock_t *lock) +{ + grp_lock_item *lck; + + grp_lock_acquire(glock); + + lck = glock->lock_list.next; + while (lck != &glock->lock_list) { + if (lck->lock == lock) + break; + lck = lck->next; + } + + if (lck != &glock->lock_list) { + unsigned i; + + pj_list_erase(lck); + for (i=0; i<glock->owner_cnt; ++i) + pj_lock_release(lck->lock); + } + + grp_lock_release(glock); + return PJ_SUCCESS; +} + +PJ_DEF(void) pj_grp_lock_dump(pj_grp_lock_t *grp_lock) +{ +#if PJ_GRP_LOCK_DEBUG + grp_lock_ref *ref = grp_lock->ref_list.next; + char info_buf[1000]; + pj_str_t info; + + info.ptr = info_buf; + info.slen = 0; + + pj_grp_lock_acquire(grp_lock); + pj_enter_critical_section(); + + while (ref != &grp_lock->ref_list && info.slen < sizeof(info_buf)) { + char *start = info.ptr + info.slen; + int max_len = sizeof(info_buf) - info.slen; + int len; + + len = pj_ansi_snprintf(start, max_len, "%s:%d ", ref->file, ref->line); + if (len < 1 || len > max_len) { + len = strlen(ref->file); + if (len > max_len - 1) + len = max_len - 1; + + memcpy(start, ref->file, len); + start[len++] = ' '; + } + + info.slen += len; + + ref = ref->next; + } + + if (ref != &grp_lock->ref_list) { + int i; + for (i=0; i<4; ++i) + info_buf[sizeof(info_buf)-i-1] = '.'; + } + info.ptr[info.slen-1] = '\0'; + + pj_leave_critical_section(); + pj_grp_lock_release(grp_lock); + + PJ_LOG(4,(THIS_FILE, "Group lock %p, ref_cnt=%d. Reference holders: %s", + grp_lock, pj_grp_lock_get_ref(grp_lock), info.ptr)); +#endif +} |