Ignore:
Timestamp:
Feb 21, 2013 11:18:36 AM (11 years ago)
Author:
bennylp
Message:

Fixed #1616: Implementation of Group lock and other foundation in PJLIB for fixing synchronization issues

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjlib/src/pj/lock.c

    r3553 r4359  
    2121#include <pj/os.h> 
    2222#include <pj/assert.h> 
     23#include <pj/log.h> 
    2324#include <pj/pool.h> 
    2425#include <pj/string.h> 
    2526#include <pj/errno.h> 
    2627 
     28#define THIS_FILE       "lock.c" 
    2729 
    2830typedef void LOCK_OBJ; 
     
    197199} 
    198200 
     201 
     202/****************************************************************************** 
     203 * Group lock 
     204 */ 
     205 
     206/* Individual lock in the group lock */ 
     207typedef struct grp_lock_item 
     208{ 
     209    PJ_DECL_LIST_MEMBER(struct grp_lock_item); 
     210    int          prio; 
     211    pj_lock_t   *lock; 
     212 
     213} grp_lock_item; 
     214 
     215/* Destroy callbacks */ 
     216typedef struct grp_destroy_callback 
     217{ 
     218    PJ_DECL_LIST_MEMBER(struct grp_destroy_callback); 
     219    void        *comp; 
     220    void        (*handler)(void*); 
     221} grp_destroy_callback; 
     222 
     223#if PJ_GRP_LOCK_DEBUG 
     224/* Store each add_ref caller */ 
     225typedef struct grp_lock_ref 
     226{ 
     227    PJ_DECL_LIST_MEMBER(struct grp_lock_ref); 
     228    const char  *file; 
     229    int          line; 
     230} grp_lock_ref; 
     231#endif 
     232 
     233/* The group lock */ 
     234struct pj_grp_lock_t 
     235{ 
     236    pj_lock_t            base; 
     237 
     238    pj_pool_t           *pool; 
     239    pj_atomic_t         *ref_cnt; 
     240    pj_lock_t           *own_lock; 
     241 
     242    pj_thread_t         *owner; 
     243    int                  owner_cnt; 
     244 
     245    grp_lock_item        lock_list; 
     246    grp_destroy_callback destroy_list; 
     247 
     248#if PJ_GRP_LOCK_DEBUG 
     249    grp_lock_ref         ref_list; 
     250    grp_lock_ref         ref_free_list; 
     251#endif 
     252}; 
     253 
     254 
     255PJ_DEF(void) pj_grp_lock_config_default(pj_grp_lock_config *cfg) 
     256{ 
     257    pj_bzero(cfg, sizeof(*cfg)); 
     258} 
     259 
     260static void grp_lock_set_owner_thread(pj_grp_lock_t *glock) 
     261{ 
     262    if (!glock->owner) { 
     263        glock->owner = pj_thread_this(); 
     264        glock->owner_cnt = 1; 
     265    } else { 
     266        pj_assert(glock->owner == pj_thread_this()); 
     267        glock->owner_cnt++; 
     268    } 
     269} 
     270 
     271static void grp_lock_unset_owner_thread(pj_grp_lock_t *glock) 
     272{ 
     273    pj_assert(glock->owner == pj_thread_this()); 
     274    pj_assert(glock->owner_cnt > 0); 
     275    if (--glock->owner_cnt <= 0) { 
     276        glock->owner = NULL; 
     277        glock->owner_cnt = 0; 
     278    } 
     279} 
     280 
     281static pj_status_t grp_lock_acquire(LOCK_OBJ *p) 
     282{ 
     283    pj_grp_lock_t *glock = (pj_grp_lock_t*)p; 
     284    grp_lock_item *lck; 
     285 
     286    pj_assert(pj_atomic_get(glock->ref_cnt) > 0); 
     287 
     288    lck = glock->lock_list.next; 
     289    while (lck != &glock->lock_list) { 
     290        pj_lock_acquire(lck->lock); 
     291        lck = lck->next; 
     292    } 
     293    grp_lock_set_owner_thread(glock); 
     294    pj_grp_lock_add_ref(glock); 
     295    return PJ_SUCCESS; 
     296} 
     297 
     298static pj_status_t grp_lock_tryacquire(LOCK_OBJ *p) 
     299{ 
     300    pj_grp_lock_t *glock = (pj_grp_lock_t*)p; 
     301    grp_lock_item *lck; 
     302 
     303    pj_assert(pj_atomic_get(glock->ref_cnt) > 0); 
     304 
     305    lck = glock->lock_list.next; 
     306    while (lck != &glock->lock_list) { 
     307        pj_status_t status = pj_lock_tryacquire(lck->lock); 
     308        if (status != PJ_SUCCESS) { 
     309            lck = lck->prev; 
     310            while (lck != &glock->lock_list) { 
     311                pj_lock_release(lck->lock); 
     312                lck = lck->prev; 
     313            } 
     314            return status; 
     315        } 
     316        lck = lck->next; 
     317    } 
     318    grp_lock_set_owner_thread(glock); 
     319    pj_grp_lock_add_ref(glock); 
     320    return PJ_SUCCESS; 
     321} 
     322 
     323static pj_status_t grp_lock_release(LOCK_OBJ *p) 
     324{ 
     325    pj_grp_lock_t *glock = (pj_grp_lock_t*)p; 
     326    grp_lock_item *lck; 
     327 
     328    grp_lock_unset_owner_thread(glock); 
     329 
     330    lck = glock->lock_list.prev; 
     331    while (lck != &glock->lock_list) { 
     332        pj_lock_release(lck->lock); 
     333        lck = lck->prev; 
     334    } 
     335    return pj_grp_lock_dec_ref(glock); 
     336} 
     337 
     338static pj_status_t grp_lock_destroy(LOCK_OBJ *p) 
     339{ 
     340    pj_grp_lock_t *glock = (pj_grp_lock_t*)p; 
     341    pj_pool_t *pool = glock->pool; 
     342    grp_lock_item *lck; 
     343    grp_destroy_callback *cb; 
     344 
     345    if (!glock->pool) { 
     346        /* already destroyed?! */ 
     347        return PJ_EINVAL; 
     348    } 
     349 
     350    /* Release all chained locks */ 
     351    lck = glock->lock_list.next; 
     352    while (lck != &glock->lock_list) { 
     353        if (lck->lock != glock->own_lock) { 
     354            unsigned i; 
     355            for (i=0; i<glock->owner_cnt; ++i) 
     356                pj_lock_release(lck->lock); 
     357        } 
     358        lck = lck->next; 
     359    } 
     360 
     361    /* Call callbacks */ 
     362    cb = glock->destroy_list.next; 
     363    while (cb != &glock->destroy_list) { 
     364        grp_destroy_callback *next = cb->next; 
     365        cb->handler(cb->comp); 
     366        cb = next; 
     367    } 
     368 
     369    pj_lock_destroy(glock->own_lock); 
     370    pj_atomic_destroy(glock->ref_cnt); 
     371    glock->pool = NULL; 
     372    pj_pool_release(pool); 
     373 
     374    return PJ_SUCCESS; 
     375} 
     376 
     377 
     378PJ_DEF(pj_status_t) pj_grp_lock_create( pj_pool_t *pool, 
     379                                        const pj_grp_lock_config *cfg, 
     380                                        pj_grp_lock_t **p_grp_lock) 
     381{ 
     382    pj_grp_lock_t *glock; 
     383    grp_lock_item *own_lock; 
     384    pj_status_t status; 
     385 
     386    PJ_ASSERT_RETURN(pool && p_grp_lock, PJ_EINVAL); 
     387 
     388    PJ_UNUSED_ARG(cfg); 
     389 
     390    pool = pj_pool_create(pool->factory, "glck%p", 512, 512, NULL); 
     391    if (!pool) 
     392        return PJ_ENOMEM; 
     393 
     394    glock = PJ_POOL_ZALLOC_T(pool, pj_grp_lock_t); 
     395    glock->base.lock_object = glock; 
     396    glock->base.acquire = &grp_lock_acquire; 
     397    glock->base.tryacquire = &grp_lock_tryacquire; 
     398    glock->base.release = &grp_lock_release; 
     399    glock->base.destroy = &grp_lock_destroy; 
     400 
     401    glock->pool = pool; 
     402    pj_list_init(&glock->lock_list); 
     403    pj_list_init(&glock->destroy_list); 
     404#if PJ_GRP_LOCK_DEBUG 
     405    pj_list_init(&glock->ref_list); 
     406    pj_list_init(&glock->ref_free_list); 
     407#endif 
     408 
     409    status = pj_atomic_create(pool, 0, &glock->ref_cnt); 
     410    if (status != PJ_SUCCESS) 
     411        goto on_error; 
     412 
     413    status = pj_lock_create_recursive_mutex(pool, pool->obj_name, 
     414                                            &glock->own_lock); 
     415    if (status != PJ_SUCCESS) 
     416        goto on_error; 
     417 
     418    own_lock = PJ_POOL_ZALLOC_T(pool, grp_lock_item); 
     419    own_lock->lock = glock->own_lock; 
     420    pj_list_push_back(&glock->lock_list, own_lock); 
     421 
     422    *p_grp_lock = glock; 
     423    return PJ_SUCCESS; 
     424 
     425on_error: 
     426    grp_lock_destroy(glock); 
     427    return status; 
     428} 
     429 
     430PJ_DEF(pj_status_t) pj_grp_lock_destroy( pj_grp_lock_t *grp_lock) 
     431{ 
     432    return grp_lock_destroy(grp_lock); 
     433} 
     434 
     435PJ_DEF(pj_status_t) pj_grp_lock_acquire( pj_grp_lock_t *grp_lock) 
     436{ 
     437    return grp_lock_acquire(grp_lock); 
     438} 
     439 
     440PJ_DEF(pj_status_t) pj_grp_lock_tryacquire( pj_grp_lock_t *grp_lock) 
     441{ 
     442    return grp_lock_tryacquire(grp_lock); 
     443} 
     444 
     445PJ_DEF(pj_status_t) pj_grp_lock_release( pj_grp_lock_t *grp_lock) 
     446{ 
     447    return grp_lock_release(grp_lock); 
     448} 
     449 
     450PJ_DEF(pj_status_t) pj_grp_lock_replace( pj_grp_lock_t *old, 
     451                                         pj_grp_lock_t *new) 
     452{ 
     453    grp_destroy_callback *ocb; 
     454 
     455    /* Move handlers from old to new */ 
     456    ocb = old->destroy_list.next; 
     457    while (ocb != &old->destroy_list) { 
     458        grp_destroy_callback *ncb; 
     459 
     460        ncb = PJ_POOL_ALLOC_T(new->pool, grp_destroy_callback); 
     461        ncb->comp = ocb->comp; 
     462        ncb->handler = ocb->handler; 
     463        pj_list_push_back(&new->destroy_list, ncb); 
     464 
     465        ocb = ocb->next; 
     466    } 
     467 
     468    pj_list_init(&old->destroy_list); 
     469 
     470    grp_lock_destroy(old); 
     471    return PJ_SUCCESS; 
     472} 
     473 
     474PJ_DEF(pj_status_t) pj_grp_lock_add_handler( pj_grp_lock_t *glock, 
     475                                             pj_pool_t *pool, 
     476                                             void *comp, 
     477                                             void (*destroy)(void *comp)) 
     478{ 
     479    grp_destroy_callback *cb; 
     480 
     481    grp_lock_acquire(glock); 
     482 
     483    if (pool == NULL) 
     484        pool = glock->pool; 
     485 
     486    cb = PJ_POOL_ZALLOC_T(pool, grp_destroy_callback); 
     487    cb->comp = comp; 
     488    cb->handler = destroy; 
     489    pj_list_push_back(&glock->destroy_list, cb); 
     490 
     491    grp_lock_release(glock); 
     492    return PJ_SUCCESS; 
     493} 
     494 
     495PJ_DEF(pj_status_t) pj_grp_lock_del_handler( pj_grp_lock_t *glock, 
     496                                             void *comp, 
     497                                             void (*destroy)(void *comp)) 
     498{ 
     499    grp_destroy_callback *cb; 
     500 
     501    grp_lock_acquire(glock); 
     502 
     503    cb = glock->destroy_list.next; 
     504    while (cb != &glock->destroy_list) { 
     505        if (cb->comp == comp && cb->handler == destroy) 
     506            break; 
     507        cb = cb->next; 
     508    } 
     509 
     510    if (cb != &glock->destroy_list) 
     511        pj_list_erase(cb); 
     512 
     513    grp_lock_release(glock); 
     514    return PJ_SUCCESS; 
     515} 
     516 
     517static pj_status_t grp_lock_add_ref(pj_grp_lock_t *glock) 
     518{ 
     519    pj_atomic_inc(glock->ref_cnt); 
     520    return PJ_SUCCESS; 
     521} 
     522 
     523static pj_status_t grp_lock_dec_ref(pj_grp_lock_t *glock) 
     524{ 
     525    int cnt; /* for debugging */ 
     526    if ((cnt=pj_atomic_dec_and_get(glock->ref_cnt)) == 0) { 
     527        grp_lock_destroy(glock); 
     528        return PJ_EGONE; 
     529    } 
     530    pj_assert(cnt > 0); 
     531    pj_grp_lock_dump(glock); 
     532    return PJ_SUCCESS; 
     533} 
     534 
     535#if PJ_GRP_LOCK_DEBUG 
     536PJ_DEF(pj_status_t) pj_grp_lock_add_ref_dbg(pj_grp_lock_t *glock, 
     537                                            const char *file, 
     538                                            int line) 
     539{ 
     540    grp_lock_ref *ref; 
     541    pj_status_t status; 
     542 
     543    pj_enter_critical_section(); 
     544    if (!pj_list_empty(&glock->ref_free_list)) { 
     545        ref = glock->ref_free_list.next; 
     546        pj_list_erase(ref); 
     547    } else { 
     548        ref = PJ_POOL_ALLOC_T(glock->pool, grp_lock_ref); 
     549    } 
     550 
     551    ref->file = file; 
     552    ref->line = line; 
     553    pj_list_push_back(&glock->ref_list, ref); 
     554 
     555    pj_leave_critical_section(); 
     556 
     557    status = grp_lock_add_ref(glock); 
     558 
     559    if (status != PJ_SUCCESS) { 
     560        pj_enter_critical_section(); 
     561        pj_list_erase(ref); 
     562        pj_list_push_back(&glock->ref_free_list, ref); 
     563        pj_leave_critical_section(); 
     564    } 
     565 
     566    return status; 
     567} 
     568 
     569PJ_DEF(pj_status_t) pj_grp_lock_dec_ref_dbg(pj_grp_lock_t *glock, 
     570                                            const char *file, 
     571                                            int line) 
     572{ 
     573    grp_lock_ref *ref; 
     574 
     575    pj_enter_critical_section(); 
     576    /* Find the same source file */ 
     577    ref = glock->ref_list.next; 
     578    while (ref != &glock->ref_list) { 
     579        if (strcmp(ref->file, file) == 0) { 
     580            pj_list_erase(ref); 
     581            pj_list_push_back(&glock->ref_free_list, ref); 
     582            break; 
     583        } 
     584        ref = ref->next; 
     585    } 
     586    pj_leave_critical_section(); 
     587 
     588    if (ref == &glock->ref_list) { 
     589        PJ_LOG(2,(THIS_FILE, "pj_grp_lock_dec_ref_dbg() could not find " 
     590                              "matching ref for %s", file)); 
     591    } 
     592 
     593    return grp_lock_dec_ref(glock); 
     594} 
     595#else 
     596PJ_DEF(pj_status_t) pj_grp_lock_add_ref(pj_grp_lock_t *glock) 
     597{ 
     598    return grp_lock_add_ref(glock); 
     599} 
     600 
     601PJ_DEF(pj_status_t) pj_grp_lock_dec_ref(pj_grp_lock_t *glock) 
     602{ 
     603    return grp_lock_dec_ref(glock); 
     604} 
     605#endif 
     606 
     607PJ_DEF(int) pj_grp_lock_get_ref(pj_grp_lock_t *glock) 
     608{ 
     609    return pj_atomic_get(glock->ref_cnt); 
     610} 
     611 
     612PJ_DEF(pj_status_t) pj_grp_lock_chain_lock( pj_grp_lock_t *glock, 
     613                                            pj_lock_t *lock, 
     614                                            int pos) 
     615{ 
     616    grp_lock_item *lck, *new_lck; 
     617    unsigned i; 
     618 
     619    grp_lock_acquire(glock); 
     620 
     621    for (i=0; i<glock->owner_cnt; ++i) 
     622        pj_lock_acquire(lock); 
     623 
     624    lck = glock->lock_list.next; 
     625    while (lck != &glock->lock_list) { 
     626        if (lck->prio >= pos) 
     627            break; 
     628        lck = lck->next; 
     629    } 
     630 
     631    new_lck = PJ_POOL_ZALLOC_T(glock->pool, grp_lock_item); 
     632    new_lck->prio = pos; 
     633    new_lck->lock = lock; 
     634    pj_list_insert_before(lck, new_lck); 
     635 
     636    /* this will also release the new lock */ 
     637    grp_lock_release(glock); 
     638    return PJ_SUCCESS; 
     639} 
     640 
     641PJ_DEF(pj_status_t) pj_grp_lock_unchain_lock( pj_grp_lock_t *glock, 
     642                                              pj_lock_t *lock) 
     643{ 
     644    grp_lock_item *lck; 
     645 
     646    grp_lock_acquire(glock); 
     647 
     648    lck = glock->lock_list.next; 
     649    while (lck != &glock->lock_list) { 
     650        if (lck->lock == lock) 
     651            break; 
     652        lck = lck->next; 
     653    } 
     654 
     655    if (lck != &glock->lock_list) { 
     656        unsigned i; 
     657 
     658        pj_list_erase(lck); 
     659        for (i=0; i<glock->owner_cnt; ++i) 
     660            pj_lock_release(lck->lock); 
     661    } 
     662 
     663    grp_lock_release(glock); 
     664    return PJ_SUCCESS; 
     665} 
     666 
     667PJ_DEF(void) pj_grp_lock_dump(pj_grp_lock_t *grp_lock) 
     668{ 
     669#if PJ_GRP_LOCK_DEBUG 
     670    grp_lock_ref *ref = grp_lock->ref_list.next; 
     671    char info_buf[1000]; 
     672    pj_str_t info; 
     673 
     674    info.ptr = info_buf; 
     675    info.slen = 0; 
     676 
     677    pj_grp_lock_acquire(grp_lock); 
     678    pj_enter_critical_section(); 
     679 
     680    while (ref != &grp_lock->ref_list && info.slen < sizeof(info_buf)) { 
     681        char *start = info.ptr + info.slen; 
     682        int max_len = sizeof(info_buf) - info.slen; 
     683        int len; 
     684 
     685        len = pj_ansi_snprintf(start, max_len, "%s:%d ", ref->file, ref->line); 
     686        if (len < 1 || len > max_len) { 
     687            len = strlen(ref->file); 
     688            if (len > max_len - 1) 
     689                len = max_len - 1; 
     690 
     691            memcpy(start, ref->file, len); 
     692            start[len++] = ' '; 
     693        } 
     694 
     695        info.slen += len; 
     696 
     697        ref = ref->next; 
     698    } 
     699 
     700    if (ref != &grp_lock->ref_list) { 
     701        int i; 
     702        for (i=0; i<4; ++i) 
     703            info_buf[sizeof(info_buf)-i-1] = '.'; 
     704    } 
     705    info.ptr[info.slen-1] = '\0'; 
     706 
     707    pj_leave_critical_section(); 
     708    pj_grp_lock_release(grp_lock); 
     709 
     710    PJ_LOG(4,(THIS_FILE, "Group lock %p, ref_cnt=%d. Reference holders: %s", 
     711               grp_lock, pj_grp_lock_get_ref(grp_lock), info.ptr)); 
     712#endif 
     713} 
Note: See TracChangeset for help on using the changeset viewer.