summaryrefslogtreecommitdiff
path: root/pjlib/src/pj/lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjlib/src/pj/lock.c')
-rw-r--r--pjlib/src/pj/lock.c519
1 files changed, 518 insertions, 1 deletions
diff --git a/pjlib/src/pj/lock.c b/pjlib/src/pj/lock.c
index a7879af..34e2d1e 100644
--- a/pjlib/src/pj/lock.c
+++ b/pjlib/src/pj/lock.c
@@ -1,4 +1,4 @@
-/* $Id: lock.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: lock.c 4412 2013-03-05 03:12:32Z riza $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -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,518 @@ 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) {
+ int 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_lock,
+ pj_grp_lock_t *new_lock)
+{
+ grp_destroy_callback *ocb;
+
+ /* Move handlers from old to new */
+ ocb = old_lock->destroy_list.next;
+ while (ocb != &old_lock->destroy_list) {
+ grp_destroy_callback *ncb;
+
+ ncb = PJ_POOL_ALLOC_T(new_lock->pool, grp_destroy_callback);
+ ncb->comp = ocb->comp;
+ ncb->handler = ocb->handler;
+ pj_list_push_back(&new_lock->destroy_list, ncb);
+
+ ocb = ocb->next;
+ }
+
+ pj_list_init(&old_lock->destroy_list);
+
+ grp_lock_destroy(old_lock);
+ 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;
+ int 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) {
+ int 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));
+#else
+ PJ_UNUSED_ARG(grp_lock);
+#endif
+}