1
/* $Id: lock.c 4412 2013-03-05 03:12:32Z riza $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
#include <pj/assert.h>
25
#include <pj/string.h>
28
#define THIS_FILE "lock.c"
30
typedef void LOCK_OBJ;
37
LOCK_OBJ *lock_object;
39
pj_status_t (*acquire) (LOCK_OBJ*);
40
pj_status_t (*tryacquire) (LOCK_OBJ*);
41
pj_status_t (*release) (LOCK_OBJ*);
42
pj_status_t (*destroy) (LOCK_OBJ*);
45
typedef pj_status_t (*FPTR)(LOCK_OBJ*);
47
/******************************************************************************
48
* Implementation of lock object with mutex.
50
static pj_lock_t mutex_lock_template =
53
(FPTR) &pj_mutex_lock,
54
(FPTR) &pj_mutex_trylock,
55
(FPTR) &pj_mutex_unlock,
56
(FPTR) &pj_mutex_destroy
59
static pj_status_t create_mutex_lock( pj_pool_t *pool,
68
PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
70
p_lock = PJ_POOL_ALLOC_T(pool, pj_lock_t);
74
pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t));
75
rc = pj_mutex_create(pool, name, type, &mutex);
79
p_lock->lock_object = mutex;
85
PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool,
89
return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock);
92
PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool,
96
return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock);
100
/******************************************************************************
101
* Implementation of NULL lock object.
103
static pj_status_t null_op(void *arg)
109
static pj_lock_t null_lock_template =
118
PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool,
125
PJ_ASSERT_RETURN(lock, PJ_EINVAL);
127
*lock = &null_lock_template;
132
/******************************************************************************
133
* Implementation of semaphore lock object.
135
#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
137
static pj_lock_t sem_lock_template =
141
(FPTR) &pj_sem_trywait,
143
(FPTR) &pj_sem_destroy
146
PJ_DEF(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool,
156
PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
158
p_lock = PJ_POOL_ALLOC_T(pool, pj_lock_t);
162
pj_memcpy(p_lock, &sem_lock_template, sizeof(pj_lock_t));
163
rc = pj_sem_create( pool, name, initial, max, &sem);
164
if (rc != PJ_SUCCESS)
167
p_lock->lock_object = sem;
174
#endif /* PJ_HAS_SEMAPHORE */
177
PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock )
179
PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
180
return (*lock->acquire)(lock->lock_object);
183
PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock )
185
PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
186
return (*lock->tryacquire)(lock->lock_object);
189
PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock )
191
PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
192
return (*lock->release)(lock->lock_object);
195
PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock )
197
PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
198
return (*lock->destroy)(lock->lock_object);
202
/******************************************************************************
206
/* Individual lock in the group lock */
207
typedef struct grp_lock_item
209
PJ_DECL_LIST_MEMBER(struct grp_lock_item);
215
/* Destroy callbacks */
216
typedef struct grp_destroy_callback
218
PJ_DECL_LIST_MEMBER(struct grp_destroy_callback);
220
void (*handler)(void*);
221
} grp_destroy_callback;
223
#if PJ_GRP_LOCK_DEBUG
224
/* Store each add_ref caller */
225
typedef struct grp_lock_ref
227
PJ_DECL_LIST_MEMBER(struct grp_lock_ref);
239
pj_atomic_t *ref_cnt;
245
grp_lock_item lock_list;
246
grp_destroy_callback destroy_list;
248
#if PJ_GRP_LOCK_DEBUG
249
grp_lock_ref ref_list;
250
grp_lock_ref ref_free_list;
255
PJ_DEF(void) pj_grp_lock_config_default(pj_grp_lock_config *cfg)
257
pj_bzero(cfg, sizeof(*cfg));
260
static void grp_lock_set_owner_thread(pj_grp_lock_t *glock)
263
glock->owner = pj_thread_this();
264
glock->owner_cnt = 1;
266
pj_assert(glock->owner == pj_thread_this());
271
static void grp_lock_unset_owner_thread(pj_grp_lock_t *glock)
273
pj_assert(glock->owner == pj_thread_this());
274
pj_assert(glock->owner_cnt > 0);
275
if (--glock->owner_cnt <= 0) {
277
glock->owner_cnt = 0;
281
static pj_status_t grp_lock_acquire(LOCK_OBJ *p)
283
pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
286
pj_assert(pj_atomic_get(glock->ref_cnt) > 0);
288
lck = glock->lock_list.next;
289
while (lck != &glock->lock_list) {
290
pj_lock_acquire(lck->lock);
293
grp_lock_set_owner_thread(glock);
294
pj_grp_lock_add_ref(glock);
298
static pj_status_t grp_lock_tryacquire(LOCK_OBJ *p)
300
pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
303
pj_assert(pj_atomic_get(glock->ref_cnt) > 0);
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) {
310
while (lck != &glock->lock_list) {
311
pj_lock_release(lck->lock);
318
grp_lock_set_owner_thread(glock);
319
pj_grp_lock_add_ref(glock);
323
static pj_status_t grp_lock_release(LOCK_OBJ *p)
325
pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
328
grp_lock_unset_owner_thread(glock);
330
lck = glock->lock_list.prev;
331
while (lck != &glock->lock_list) {
332
pj_lock_release(lck->lock);
335
return pj_grp_lock_dec_ref(glock);
338
static pj_status_t grp_lock_destroy(LOCK_OBJ *p)
340
pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
341
pj_pool_t *pool = glock->pool;
343
grp_destroy_callback *cb;
346
/* already destroyed?! */
350
/* Release all chained locks */
351
lck = glock->lock_list.next;
352
while (lck != &glock->lock_list) {
353
if (lck->lock != glock->own_lock) {
355
for (i=0; i<glock->owner_cnt; ++i)
356
pj_lock_release(lck->lock);
362
cb = glock->destroy_list.next;
363
while (cb != &glock->destroy_list) {
364
grp_destroy_callback *next = cb->next;
365
cb->handler(cb->comp);
369
pj_lock_destroy(glock->own_lock);
370
pj_atomic_destroy(glock->ref_cnt);
372
pj_pool_release(pool);
378
PJ_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)
382
pj_grp_lock_t *glock;
383
grp_lock_item *own_lock;
386
PJ_ASSERT_RETURN(pool && p_grp_lock, PJ_EINVAL);
390
pool = pj_pool_create(pool->factory, "glck%p", 512, 512, NULL);
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;
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);
409
status = pj_atomic_create(pool, 0, &glock->ref_cnt);
410
if (status != PJ_SUCCESS)
413
status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
415
if (status != PJ_SUCCESS)
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);
426
grp_lock_destroy(glock);
430
PJ_DEF(pj_status_t) pj_grp_lock_destroy( pj_grp_lock_t *grp_lock)
432
return grp_lock_destroy(grp_lock);
435
PJ_DEF(pj_status_t) pj_grp_lock_acquire( pj_grp_lock_t *grp_lock)
437
return grp_lock_acquire(grp_lock);
440
PJ_DEF(pj_status_t) pj_grp_lock_tryacquire( pj_grp_lock_t *grp_lock)
442
return grp_lock_tryacquire(grp_lock);
445
PJ_DEF(pj_status_t) pj_grp_lock_release( pj_grp_lock_t *grp_lock)
447
return grp_lock_release(grp_lock);
450
PJ_DEF(pj_status_t) pj_grp_lock_replace( pj_grp_lock_t *old_lock,
451
pj_grp_lock_t *new_lock)
453
grp_destroy_callback *ocb;
455
/* Move handlers from old to new */
456
ocb = old_lock->destroy_list.next;
457
while (ocb != &old_lock->destroy_list) {
458
grp_destroy_callback *ncb;
460
ncb = PJ_POOL_ALLOC_T(new_lock->pool, grp_destroy_callback);
461
ncb->comp = ocb->comp;
462
ncb->handler = ocb->handler;
463
pj_list_push_back(&new_lock->destroy_list, ncb);
468
pj_list_init(&old_lock->destroy_list);
470
grp_lock_destroy(old_lock);
474
PJ_DEF(pj_status_t) pj_grp_lock_add_handler( pj_grp_lock_t *glock,
477
void (*destroy)(void *comp))
479
grp_destroy_callback *cb;
481
grp_lock_acquire(glock);
486
cb = PJ_POOL_ZALLOC_T(pool, grp_destroy_callback);
488
cb->handler = destroy;
489
pj_list_push_back(&glock->destroy_list, cb);
491
grp_lock_release(glock);
495
PJ_DEF(pj_status_t) pj_grp_lock_del_handler( pj_grp_lock_t *glock,
497
void (*destroy)(void *comp))
499
grp_destroy_callback *cb;
501
grp_lock_acquire(glock);
503
cb = glock->destroy_list.next;
504
while (cb != &glock->destroy_list) {
505
if (cb->comp == comp && cb->handler == destroy)
510
if (cb != &glock->destroy_list)
513
grp_lock_release(glock);
517
static pj_status_t grp_lock_add_ref(pj_grp_lock_t *glock)
519
pj_atomic_inc(glock->ref_cnt);
523
static pj_status_t grp_lock_dec_ref(pj_grp_lock_t *glock)
525
int cnt; /* for debugging */
526
if ((cnt=pj_atomic_dec_and_get(glock->ref_cnt)) == 0) {
527
grp_lock_destroy(glock);
531
pj_grp_lock_dump(glock);
535
#if PJ_GRP_LOCK_DEBUG
536
PJ_DEF(pj_status_t) pj_grp_lock_add_ref_dbg(pj_grp_lock_t *glock,
543
pj_enter_critical_section();
544
if (!pj_list_empty(&glock->ref_free_list)) {
545
ref = glock->ref_free_list.next;
548
ref = PJ_POOL_ALLOC_T(glock->pool, grp_lock_ref);
553
pj_list_push_back(&glock->ref_list, ref);
555
pj_leave_critical_section();
557
status = grp_lock_add_ref(glock);
559
if (status != PJ_SUCCESS) {
560
pj_enter_critical_section();
562
pj_list_push_back(&glock->ref_free_list, ref);
563
pj_leave_critical_section();
569
PJ_DEF(pj_status_t) pj_grp_lock_dec_ref_dbg(pj_grp_lock_t *glock,
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) {
581
pj_list_push_back(&glock->ref_free_list, ref);
586
pj_leave_critical_section();
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));
593
return grp_lock_dec_ref(glock);
596
PJ_DEF(pj_status_t) pj_grp_lock_add_ref(pj_grp_lock_t *glock)
598
return grp_lock_add_ref(glock);
601
PJ_DEF(pj_status_t) pj_grp_lock_dec_ref(pj_grp_lock_t *glock)
603
return grp_lock_dec_ref(glock);
607
PJ_DEF(int) pj_grp_lock_get_ref(pj_grp_lock_t *glock)
609
return pj_atomic_get(glock->ref_cnt);
612
PJ_DEF(pj_status_t) pj_grp_lock_chain_lock( pj_grp_lock_t *glock,
616
grp_lock_item *lck, *new_lck;
619
grp_lock_acquire(glock);
621
for (i=0; i<glock->owner_cnt; ++i)
622
pj_lock_acquire(lock);
624
lck = glock->lock_list.next;
625
while (lck != &glock->lock_list) {
626
if (lck->prio >= pos)
631
new_lck = PJ_POOL_ZALLOC_T(glock->pool, grp_lock_item);
633
new_lck->lock = lock;
634
pj_list_insert_before(lck, new_lck);
636
/* this will also release the new lock */
637
grp_lock_release(glock);
641
PJ_DEF(pj_status_t) pj_grp_lock_unchain_lock( pj_grp_lock_t *glock,
646
grp_lock_acquire(glock);
648
lck = glock->lock_list.next;
649
while (lck != &glock->lock_list) {
650
if (lck->lock == lock)
655
if (lck != &glock->lock_list) {
659
for (i=0; i<glock->owner_cnt; ++i)
660
pj_lock_release(lck->lock);
663
grp_lock_release(glock);
667
PJ_DEF(void) pj_grp_lock_dump(pj_grp_lock_t *grp_lock)
669
#if PJ_GRP_LOCK_DEBUG
670
grp_lock_ref *ref = grp_lock->ref_list.next;
677
pj_grp_lock_acquire(grp_lock);
678
pj_enter_critical_section();
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;
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)
691
memcpy(start, ref->file, len);
700
if (ref != &grp_lock->ref_list) {
703
info_buf[sizeof(info_buf)-i-1] = '.';
705
info.ptr[info.slen-1] = '\0';
707
pj_leave_critical_section();
708
pj_grp_lock_release(grp_lock);
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));
713
PJ_UNUSED_ARG(grp_lock);