blob: efc07879441bd50f9c18d6776092289332aa80a0 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id: lock.c 4613 2013-10-08 09:08:13Z bennylp $ */
Tristan Matthews0a329cc2013-07-17 13:20:14 -04002/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
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.
10 *
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.
15 *
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
19 */
20#include <pj/lock.h>
21#include <pj/os.h>
22#include <pj/assert.h>
23#include <pj/log.h>
24#include <pj/pool.h>
25#include <pj/string.h>
26#include <pj/errno.h>
27
28#define THIS_FILE "lock.c"
29
30typedef void LOCK_OBJ;
31
32/*
33 * Lock structure.
34 */
35struct pj_lock_t
36{
37 LOCK_OBJ *lock_object;
38
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*);
43};
44
45typedef pj_status_t (*FPTR)(LOCK_OBJ*);
46
47/******************************************************************************
48 * Implementation of lock object with mutex.
49 */
50static pj_lock_t mutex_lock_template =
51{
52 NULL,
53 (FPTR) &pj_mutex_lock,
54 (FPTR) &pj_mutex_trylock,
55 (FPTR) &pj_mutex_unlock,
56 (FPTR) &pj_mutex_destroy
57};
58
59static pj_status_t create_mutex_lock( pj_pool_t *pool,
60 const char *name,
61 int type,
62 pj_lock_t **lock )
63{
64 pj_lock_t *p_lock;
65 pj_mutex_t *mutex;
66 pj_status_t rc;
67
68 PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
69
70 p_lock = PJ_POOL_ALLOC_T(pool, pj_lock_t);
71 if (!p_lock)
72 return PJ_ENOMEM;
73
74 pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t));
75 rc = pj_mutex_create(pool, name, type, &mutex);
76 if (rc != PJ_SUCCESS)
77 return rc;
78
79 p_lock->lock_object = mutex;
80 *lock = p_lock;
81 return PJ_SUCCESS;
82}
83
84
85PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool,
86 const char *name,
87 pj_lock_t **lock )
88{
89 return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock);
90}
91
92PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool,
93 const char *name,
94 pj_lock_t **lock )
95{
96 return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock);
97}
98
99
100/******************************************************************************
101 * Implementation of NULL lock object.
102 */
103static pj_status_t null_op(void *arg)
104{
105 PJ_UNUSED_ARG(arg);
106 return PJ_SUCCESS;
107}
108
109static pj_lock_t null_lock_template =
110{
111 NULL,
112 &null_op,
113 &null_op,
114 &null_op,
115 &null_op
116};
117
118PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool,
119 const char *name,
120 pj_lock_t **lock )
121{
122 PJ_UNUSED_ARG(name);
123 PJ_UNUSED_ARG(pool);
124
125 PJ_ASSERT_RETURN(lock, PJ_EINVAL);
126
127 *lock = &null_lock_template;
128 return PJ_SUCCESS;
129}
130
131
132/******************************************************************************
133 * Implementation of semaphore lock object.
134 */
135#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
136
137static pj_lock_t sem_lock_template =
138{
139 NULL,
140 (FPTR) &pj_sem_wait,
141 (FPTR) &pj_sem_trywait,
142 (FPTR) &pj_sem_post,
143 (FPTR) &pj_sem_destroy
144};
145
146PJ_DEF(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool,
147 const char *name,
148 unsigned initial,
149 unsigned max,
150 pj_lock_t **lock )
151{
152 pj_lock_t *p_lock;
153 pj_sem_t *sem;
154 pj_status_t rc;
155
156 PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
157
158 p_lock = PJ_POOL_ALLOC_T(pool, pj_lock_t);
159 if (!p_lock)
160 return PJ_ENOMEM;
161
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)
165 return rc;
166
167 p_lock->lock_object = sem;
168 *lock = p_lock;
169
170 return PJ_SUCCESS;
171}
172
173
174#endif /* PJ_HAS_SEMAPHORE */
175
176
177PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock )
178{
179 PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
180 return (*lock->acquire)(lock->lock_object);
181}
182
183PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock )
184{
185 PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
186 return (*lock->tryacquire)(lock->lock_object);
187}
188
189PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock )
190{
191 PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
192 return (*lock->release)(lock->lock_object);
193}
194
195PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock )
196{
197 PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
198 return (*lock->destroy)(lock->lock_object);
199}
200
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 int 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_lock,
451 pj_grp_lock_t *new_lock)
452{
453 grp_destroy_callback *ocb;
454
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;
459
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);
464
465 ocb = ocb->next;
466 }
467
468 pj_list_init(&old_lock->destroy_list);
469
470 grp_lock_destroy(old_lock);
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 int 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 int 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);
Alexandre Lision8af73cb2013-12-10 14:11:20 -0500686 if (len < 1 || len >= max_len) {
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400687 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#else
713 PJ_UNUSED_ARG(grp_lock);
714#endif
715}