Coverage Report

Created: 2025-07-11 06:15

/src/pjsip/pjlib/src/pj/lock.c
Line
Count
Source (jump to first uncovered line)
1
/* 
2
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
3
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
18
 */
19
#include <pj/lock.h>
20
#include <pj/os.h>
21
#include <pj/assert.h>
22
#include <pj/log.h>
23
#include <pj/pool.h>
24
#include <pj/string.h>
25
#include <pj/errno.h>
26
27
#define THIS_FILE       "lock.c"
28
29
typedef void LOCK_OBJ;
30
31
/*
32
 * Lock structure.
33
 */
34
struct pj_lock_t
35
{
36
    LOCK_OBJ *lock_object;
37
38
    pj_status_t (*acquire)      (LOCK_OBJ*);
39
    pj_status_t (*tryacquire)   (LOCK_OBJ*);
40
    pj_status_t (*release)      (LOCK_OBJ*);
41
    pj_status_t (*destroy)      (LOCK_OBJ*);
42
};
43
44
typedef pj_status_t (*FPTR)(LOCK_OBJ*);
45
46
/******************************************************************************
47
 * Implementation of lock object with mutex.
48
 */
49
static pj_lock_t mutex_lock_template = 
50
{
51
    NULL,
52
    (FPTR) &pj_mutex_lock,
53
    (FPTR) &pj_mutex_trylock,
54
    (FPTR) &pj_mutex_unlock,
55
    (FPTR) &pj_mutex_destroy
56
};
57
58
static pj_status_t create_mutex_lock( pj_pool_t *pool,
59
                                      const char *name,
60
                                      int type,
61
                                      pj_lock_t **lock )
62
216
{
63
216
    pj_lock_t *p_lock;
64
216
    pj_mutex_t *mutex;
65
216
    pj_status_t rc;
66
67
216
    PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
68
69
216
    p_lock = PJ_POOL_ALLOC_T(pool, pj_lock_t);
70
216
    if (!p_lock)
71
0
        return PJ_ENOMEM;
72
73
216
    pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t));
74
216
    rc = pj_mutex_create(pool, name, type, &mutex);
75
216
    if (rc != PJ_SUCCESS)
76
0
        return rc;
77
78
216
    p_lock->lock_object = mutex;
79
216
    *lock = p_lock;
80
216
    return PJ_SUCCESS;
81
216
}
82
83
84
PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool,
85
                                                 const char *name,
86
                                                 pj_lock_t **lock )
87
216
{
88
216
    return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock);
89
216
}
90
91
PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool,
92
                                                    const char *name,
93
                                                    pj_lock_t **lock )
94
0
{
95
0
    return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock);
96
0
}
97
98
99
/******************************************************************************
100
 * Implementation of NULL lock object.
101
 */
102
static pj_status_t null_op(void *arg)
103
0
{
104
0
    PJ_UNUSED_ARG(arg);
105
0
    return PJ_SUCCESS;
106
0
}
107
108
static pj_lock_t null_lock_template = 
109
{
110
    NULL,
111
    &null_op,
112
    &null_op,
113
    &null_op,
114
    &null_op
115
};
116
117
PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool,
118
                                               const char *name,
119
                                               pj_lock_t **lock )
120
216
{
121
216
    PJ_UNUSED_ARG(name);
122
216
    PJ_UNUSED_ARG(pool);
123
124
216
    PJ_ASSERT_RETURN(lock, PJ_EINVAL);
125
126
216
    *lock = &null_lock_template;
127
216
    return PJ_SUCCESS;
128
216
}
129
130
131
/******************************************************************************
132
 * Implementation of semaphore lock object.
133
 */
134
#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
135
136
static pj_lock_t sem_lock_template = 
137
{
138
    NULL,
139
    (FPTR) &pj_sem_wait,
140
    (FPTR) &pj_sem_trywait,
141
    (FPTR) &pj_sem_post,
142
    (FPTR) &pj_sem_destroy
143
};
144
145
PJ_DEF(pj_status_t) pj_lock_create_semaphore(  pj_pool_t *pool,
146
                                               const char *name,
147
                                               unsigned initial,
148
                                               unsigned max,
149
                                               pj_lock_t **lock )
150
0
{
151
0
    pj_lock_t *p_lock;
152
0
    pj_sem_t *sem;
153
0
    pj_status_t rc;
154
155
0
    PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
156
157
0
    p_lock = PJ_POOL_ALLOC_T(pool, pj_lock_t);
158
0
    if (!p_lock)
159
0
        return PJ_ENOMEM;
160
161
0
    pj_memcpy(p_lock, &sem_lock_template, sizeof(pj_lock_t));
162
0
    rc = pj_sem_create( pool, name, initial, max, &sem);
163
0
    if (rc != PJ_SUCCESS)
164
0
        return rc;
165
166
0
    p_lock->lock_object = sem;
167
0
    *lock = p_lock;
168
169
0
    return PJ_SUCCESS;
170
0
}
171
172
173
#endif  /* PJ_HAS_SEMAPHORE */
174
175
176
PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock )
177
0
{
178
0
    PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
179
0
    return (*lock->acquire)(lock->lock_object);
180
0
}
181
182
PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock )
183
0
{
184
0
    PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
185
0
    return (*lock->tryacquire)(lock->lock_object);
186
0
}
187
188
PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock )
189
0
{
190
0
    PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
191
0
    return (*lock->release)(lock->lock_object);
192
0
}
193
194
PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock )
195
216
{
196
216
    PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
197
216
    return (*lock->destroy)(lock->lock_object);
198
216
}
199
200
201
/******************************************************************************
202
 * Group lock
203
 */
204
205
/* Individual lock in the group lock */
206
typedef struct grp_lock_item
207
{
208
    PJ_DECL_LIST_MEMBER(struct grp_lock_item);
209
    int          prio;
210
    pj_lock_t   *lock;
211
212
} grp_lock_item;
213
214
/* Destroy callbacks */
215
typedef struct grp_destroy_callback
216
{
217
    PJ_DECL_LIST_MEMBER(struct grp_destroy_callback);
218
    void        *comp;
219
    void        (*handler)(void*);
220
} grp_destroy_callback;
221
222
#if PJ_GRP_LOCK_DEBUG
223
/* Store each add_ref caller */
224
typedef struct grp_lock_ref
225
{
226
    PJ_DECL_LIST_MEMBER(struct grp_lock_ref);
227
    const char  *file;
228
    int          line;
229
} grp_lock_ref;
230
#endif
231
232
/* The group lock */
233
struct pj_grp_lock_t
234
{
235
    pj_lock_t            base;
236
237
    pj_pool_t           *pool;
238
    pj_atomic_t         *ref_cnt;
239
    pj_lock_t           *own_lock;
240
241
    pj_thread_t         *owner;
242
    int                  owner_cnt;
243
244
    grp_lock_item        lock_list;
245
    grp_destroy_callback destroy_list;
246
247
#if PJ_GRP_LOCK_DEBUG
248
    grp_lock_ref         ref_list;
249
    grp_lock_ref         ref_free_list;
250
#endif
251
};
252
253
254
PJ_DEF(void) pj_grp_lock_config_default(pj_grp_lock_config *cfg)
255
0
{
256
0
    pj_bzero(cfg, sizeof(*cfg));
257
0
}
258
259
static void grp_lock_set_owner_thread(pj_grp_lock_t *glock)
260
0
{
261
0
    if (!glock->owner) {
262
0
#if PJ_HAS_THREADS
263
0
        glock->owner = pj_thread_this();
264
#else
265
        glock->owner = (pj_thread_t *) -1;
266
#endif
267
0
        glock->owner_cnt = 1;
268
0
    } else {
269
0
#if PJ_HAS_THREADS
270
0
        pj_assert(glock->owner == pj_thread_this());
271
0
#endif
272
0
        glock->owner_cnt++;
273
0
    }
274
0
}
275
276
static void grp_lock_unset_owner_thread(pj_grp_lock_t *glock)
277
0
{
278
0
#if PJ_HAS_THREADS
279
0
    pj_assert(glock->owner == pj_thread_this());
280
0
#endif
281
0
    pj_assert(glock->owner_cnt > 0);
282
0
    if (--glock->owner_cnt <= 0) {
283
0
        glock->owner = NULL;
284
0
        glock->owner_cnt = 0;
285
0
    }
286
0
}
287
288
static pj_status_t grp_lock_acquire(LOCK_OBJ *p)
289
0
{
290
0
    pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
291
0
    grp_lock_item *lck;
292
293
0
    pj_assert(pj_atomic_get(glock->ref_cnt) > 0);
294
295
0
    lck = glock->lock_list.next;
296
0
    while (lck != &glock->lock_list) {
297
0
        pj_lock_acquire(lck->lock);
298
0
        lck = lck->next;
299
0
    }
300
0
    grp_lock_set_owner_thread(glock);
301
0
    pj_grp_lock_add_ref(glock);
302
0
    return PJ_SUCCESS;
303
0
}
304
305
static pj_status_t grp_lock_tryacquire(LOCK_OBJ *p)
306
0
{
307
0
    pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
308
0
    grp_lock_item *lck;
309
310
0
    pj_assert(pj_atomic_get(glock->ref_cnt) > 0);
311
312
0
    lck = glock->lock_list.next;
313
0
    while (lck != &glock->lock_list) {
314
0
        pj_status_t status = pj_lock_tryacquire(lck->lock);
315
0
        if (status != PJ_SUCCESS) {
316
0
            lck = lck->prev;
317
0
            while (lck != &glock->lock_list) {
318
0
                pj_lock_release(lck->lock);
319
0
                lck = lck->prev;
320
0
            }
321
0
            return status;
322
0
        }
323
0
        lck = lck->next;
324
0
    }
325
0
    grp_lock_set_owner_thread(glock);
326
0
    pj_grp_lock_add_ref(glock);
327
0
    return PJ_SUCCESS;
328
0
}
329
330
static pj_status_t grp_lock_release(LOCK_OBJ *p)
331
0
{
332
0
    pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
333
0
    grp_lock_item *lck;
334
335
0
    grp_lock_unset_owner_thread(glock);
336
337
0
    lck = glock->lock_list.prev;
338
0
    while (lck != &glock->lock_list) {
339
0
        pj_lock_release(lck->lock);
340
0
        lck = lck->prev;
341
0
    }
342
0
    return pj_grp_lock_dec_ref(glock);
343
0
}
344
345
static pj_status_t grp_lock_add_handler( pj_grp_lock_t *glock,
346
                                         pj_pool_t *pool,
347
                                         void *comp,
348
                                         void (*destroy)(void *comp),
349
                                         pj_bool_t acquire_lock)
350
0
{
351
0
    grp_destroy_callback *cb;
352
353
0
    if (acquire_lock)
354
0
        grp_lock_acquire(glock);
355
356
0
    if (pool == NULL)
357
0
        pool = glock->pool;
358
359
0
    cb = PJ_POOL_ZALLOC_T(pool, grp_destroy_callback);
360
0
    cb->comp = comp;
361
0
    cb->handler = destroy;
362
0
    pj_list_push_back(&glock->destroy_list, cb);
363
364
0
    if (acquire_lock)
365
0
        grp_lock_release(glock);
366
367
0
    return PJ_SUCCESS;
368
0
}
369
370
static pj_status_t grp_lock_destroy(LOCK_OBJ *p)
371
0
{
372
0
    pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
373
0
    pj_pool_t *pool = glock->pool;
374
0
    grp_lock_item *lck;
375
0
    grp_destroy_callback *cb;
376
377
0
    if (!glock->pool) {
378
        /* already destroyed?! */
379
0
        return PJ_EINVAL;
380
0
    }
381
382
    /* Release all chained locks */
383
0
    lck = glock->lock_list.next;
384
0
    while (lck != &glock->lock_list) {
385
0
        if (lck->lock != glock->own_lock) {
386
0
            int i;
387
0
            for (i=0; i<glock->owner_cnt; ++i)
388
0
                pj_lock_release(lck->lock);
389
0
        }
390
0
        lck = lck->next;
391
0
    }
392
393
    /* Call callbacks */
394
0
    cb = glock->destroy_list.next;
395
0
    while (cb != &glock->destroy_list) {
396
0
        grp_destroy_callback *next = cb->next;
397
0
        cb->handler(cb->comp);
398
0
        cb = next;
399
0
    }
400
401
0
    pj_lock_destroy(glock->own_lock);
402
0
    pj_atomic_destroy(glock->ref_cnt);
403
0
    glock->pool = NULL;
404
0
    pj_pool_release(pool);
405
406
0
    return PJ_SUCCESS;
407
0
}
408
409
410
PJ_DEF(pj_status_t) pj_grp_lock_create( pj_pool_t *pool,
411
                                        const pj_grp_lock_config *cfg,
412
                                        pj_grp_lock_t **p_grp_lock)
413
0
{
414
0
    pj_grp_lock_t *glock;
415
0
    grp_lock_item *own_lock;
416
0
    pj_status_t status;
417
418
0
    PJ_ASSERT_RETURN(pool && p_grp_lock, PJ_EINVAL);
419
420
0
    PJ_UNUSED_ARG(cfg);
421
422
0
    pool = pj_pool_create(pool->factory, "glck%p", 512, 512, NULL);
423
0
    if (!pool)
424
0
        return PJ_ENOMEM;
425
426
0
    glock = PJ_POOL_ZALLOC_T(pool, pj_grp_lock_t);
427
0
    glock->base.lock_object = glock;
428
0
    glock->base.acquire = &grp_lock_acquire;
429
0
    glock->base.tryacquire = &grp_lock_tryacquire;
430
0
    glock->base.release = &grp_lock_release;
431
0
    glock->base.destroy = &grp_lock_destroy;
432
433
0
    glock->pool = pool;
434
0
    pj_list_init(&glock->lock_list);
435
0
    pj_list_init(&glock->destroy_list);
436
#if PJ_GRP_LOCK_DEBUG
437
    pj_list_init(&glock->ref_list);
438
    pj_list_init(&glock->ref_free_list);
439
#endif
440
441
0
    status = pj_atomic_create(pool, 0, &glock->ref_cnt);
442
0
    if (status != PJ_SUCCESS)
443
0
        goto on_error;
444
445
0
    status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
446
0
                                            &glock->own_lock);
447
0
    if (status != PJ_SUCCESS)
448
0
        goto on_error;
449
450
0
    own_lock = PJ_POOL_ZALLOC_T(pool, grp_lock_item);
451
0
    own_lock->lock = glock->own_lock;
452
0
    pj_list_push_back(&glock->lock_list, own_lock);
453
454
0
    *p_grp_lock = glock;
455
0
    return PJ_SUCCESS;
456
457
0
on_error:
458
0
    grp_lock_destroy(glock);
459
0
    return status;
460
0
}
461
462
PJ_DEF(pj_status_t) pj_grp_lock_create_w_handler( pj_pool_t *pool,
463
                                                  const pj_grp_lock_config *cfg,
464
                                                  void *member,
465
                                                  void (*handler)(void *member),
466
                                                  pj_grp_lock_t **p_grp_lock)
467
0
{
468
0
    pj_status_t status;
469
470
0
    status = pj_grp_lock_create(pool, cfg, p_grp_lock);
471
0
    if (status == PJ_SUCCESS) {
472
0
        pj_pool_t *grppool = (*p_grp_lock)->pool;
473
0
        grp_lock_add_handler(*p_grp_lock, grppool, member, handler, PJ_FALSE);
474
0
    }
475
    
476
0
    return status;
477
0
}
478
479
PJ_DEF(pj_status_t) pj_grp_lock_destroy( pj_grp_lock_t *grp_lock)
480
0
{
481
0
    return grp_lock_destroy(grp_lock);
482
0
}
483
484
PJ_DEF(pj_status_t) pj_grp_lock_acquire( pj_grp_lock_t *grp_lock)
485
0
{
486
0
    return grp_lock_acquire(grp_lock);
487
0
}
488
489
PJ_DEF(pj_status_t) pj_grp_lock_tryacquire( pj_grp_lock_t *grp_lock)
490
0
{
491
0
    return grp_lock_tryacquire(grp_lock);
492
0
}
493
494
PJ_DEF(pj_status_t) pj_grp_lock_release( pj_grp_lock_t *grp_lock)
495
0
{
496
0
    return grp_lock_release(grp_lock);
497
0
}
498
499
PJ_DEF(pj_status_t) pj_grp_lock_replace( pj_grp_lock_t *old_lock,
500
                                         pj_grp_lock_t *new_lock)
501
0
{
502
0
    grp_destroy_callback *ocb;
503
504
    /* Move handlers from old to new */
505
0
    ocb = old_lock->destroy_list.next;
506
0
    while (ocb != &old_lock->destroy_list) {
507
0
        grp_destroy_callback *ncb;
508
509
0
        ncb = PJ_POOL_ALLOC_T(new_lock->pool, grp_destroy_callback);
510
0
        ncb->comp = ocb->comp;
511
0
        ncb->handler = ocb->handler;
512
0
        pj_list_push_back(&new_lock->destroy_list, ncb);
513
514
0
        ocb = ocb->next;
515
0
    }
516
517
0
    pj_list_init(&old_lock->destroy_list);
518
519
0
    grp_lock_destroy(old_lock);
520
0
    return PJ_SUCCESS;
521
0
}
522
523
PJ_DEF(pj_status_t) pj_grp_lock_add_handler( pj_grp_lock_t *glock,
524
                                             pj_pool_t *pool,
525
                                             void *comp,
526
                                             void (*destroy)(void *comp))
527
0
{
528
0
    return grp_lock_add_handler(glock, pool, comp, destroy, PJ_TRUE);
529
0
}
530
531
PJ_DEF(pj_status_t) pj_grp_lock_del_handler( pj_grp_lock_t *glock,
532
                                             void *comp,
533
                                             void (*destroy)(void *comp))
534
0
{
535
0
    grp_destroy_callback *cb;
536
537
0
    grp_lock_acquire(glock);
538
539
0
    cb = glock->destroy_list.next;
540
0
    while (cb != &glock->destroy_list) {
541
0
        if (cb->comp == comp && cb->handler == destroy)
542
0
            break;
543
0
        cb = cb->next;
544
0
    }
545
546
0
    if (cb != &glock->destroy_list)
547
0
        pj_list_erase(cb);
548
549
0
    grp_lock_release(glock);
550
0
    return PJ_SUCCESS;
551
0
}
552
553
static pj_status_t grp_lock_add_ref(pj_grp_lock_t *glock)
554
0
{
555
0
    pj_atomic_inc(glock->ref_cnt);
556
0
    return PJ_SUCCESS;
557
0
}
558
559
static pj_status_t grp_lock_dec_ref(pj_grp_lock_t *glock)
560
0
{
561
0
    int cnt; /* for debugging */
562
0
    if ((cnt=pj_atomic_dec_and_get(glock->ref_cnt)) == 0) {
563
0
        grp_lock_destroy(glock);
564
0
        return PJ_EGONE;
565
0
    }
566
0
    pj_assert(cnt > 0);
567
0
    return PJ_SUCCESS;
568
0
}
569
570
#if PJ_GRP_LOCK_DEBUG
571
static pj_status_t grp_lock_dec_ref_dump(pj_grp_lock_t *glock)
572
{
573
    pj_status_t status;
574
575
    status = grp_lock_dec_ref(glock);
576
    if (status == PJ_SUCCESS) {
577
        pj_grp_lock_dump(glock);
578
    } else if (status == PJ_EGONE) {
579
        PJ_LOG(4,(THIS_FILE, "Group lock %p destroyed.", glock));
580
    }
581
582
    return status;
583
}
584
585
PJ_DEF(pj_status_t) pj_grp_lock_add_ref_dbg(pj_grp_lock_t *glock,
586
                                            const char *file,
587
                                            int line)
588
{
589
    grp_lock_ref *ref;
590
    pj_status_t status;
591
592
    pj_enter_critical_section();
593
    if (!pj_list_empty(&glock->ref_free_list)) {
594
        ref = glock->ref_free_list.next;
595
        pj_list_erase(ref);
596
    } else {
597
        ref = PJ_POOL_ALLOC_T(glock->pool, grp_lock_ref);
598
    }
599
600
    ref->file = file;
601
    ref->line = line;
602
    pj_list_push_back(&glock->ref_list, ref);
603
604
    pj_leave_critical_section();
605
606
    status = grp_lock_add_ref(glock);
607
608
    if (status != PJ_SUCCESS) {
609
        pj_enter_critical_section();
610
        pj_list_erase(ref);
611
        pj_list_push_back(&glock->ref_free_list, ref);
612
        pj_leave_critical_section();
613
    }
614
615
    return status;
616
}
617
618
PJ_DEF(pj_status_t) pj_grp_lock_dec_ref_dbg(pj_grp_lock_t *glock,
619
                                            const char *file,
620
                                            int line)
621
{
622
    grp_lock_ref *ref;
623
624
    PJ_UNUSED_ARG(line);
625
626
    pj_enter_critical_section();
627
    /* Find the same source file */
628
    ref = glock->ref_list.next;
629
    while (ref != &glock->ref_list) {
630
        if (strcmp(ref->file, file) == 0) {
631
            pj_list_erase(ref);
632
            pj_list_push_back(&glock->ref_free_list, ref);
633
            break;
634
        }
635
        ref = ref->next;
636
    }
637
    pj_leave_critical_section();
638
639
    if (ref == &glock->ref_list) {
640
        PJ_LOG(2,(THIS_FILE, "pj_grp_lock_dec_ref_dbg() could not find "
641
                              "matching ref for %s", file));
642
    }
643
644
    return grp_lock_dec_ref_dump(glock);
645
}
646
#else
647
PJ_DEF(pj_status_t) pj_grp_lock_add_ref(pj_grp_lock_t *glock)
648
0
{
649
0
    return grp_lock_add_ref(glock);
650
0
}
651
652
PJ_DEF(pj_status_t) pj_grp_lock_dec_ref(pj_grp_lock_t *glock)
653
0
{
654
0
    return grp_lock_dec_ref(glock);
655
0
}
656
#endif
657
658
PJ_DEF(int) pj_grp_lock_get_ref(pj_grp_lock_t *glock)
659
0
{
660
0
    return pj_atomic_get(glock->ref_cnt);
661
0
}
662
663
PJ_DEF(pj_status_t) pj_grp_lock_chain_lock( pj_grp_lock_t *glock,
664
                                            pj_lock_t *lock,
665
                                            int pos)
666
0
{
667
0
    grp_lock_item *lck, *new_lck;
668
0
    int i;
669
670
0
    grp_lock_acquire(glock);
671
672
0
    for (i=0; i<glock->owner_cnt; ++i)
673
0
        pj_lock_acquire(lock);
674
675
0
    lck = glock->lock_list.next;
676
0
    while (lck != &glock->lock_list) {
677
0
        if (lck->prio >= pos)
678
0
            break;
679
0
        lck = lck->next;
680
0
    }
681
682
0
    new_lck = PJ_POOL_ZALLOC_T(glock->pool, grp_lock_item);
683
0
    new_lck->prio = pos;
684
0
    new_lck->lock = lock;
685
0
    pj_list_insert_before(lck, new_lck);
686
687
    /* this will also release the new lock */
688
0
    grp_lock_release(glock);
689
0
    return PJ_SUCCESS;
690
0
}
691
692
PJ_DEF(pj_status_t) pj_grp_lock_unchain_lock( pj_grp_lock_t *glock,
693
                                              pj_lock_t *lock)
694
0
{
695
0
    grp_lock_item *lck;
696
697
0
    grp_lock_acquire(glock);
698
699
0
    lck = glock->lock_list.next;
700
0
    while (lck != &glock->lock_list) {
701
0
        if (lck->lock == lock)
702
0
            break;
703
0
        lck = lck->next;
704
0
    }
705
706
0
    if (lck != &glock->lock_list) {
707
0
        int i;
708
709
0
        pj_list_erase(lck);
710
0
        for (i=0; i<glock->owner_cnt; ++i)
711
0
            pj_lock_release(lck->lock);
712
0
    }
713
714
0
    grp_lock_release(glock);
715
0
    return PJ_SUCCESS;
716
0
}
717
718
PJ_DEF(void) pj_grp_lock_dump(pj_grp_lock_t *grp_lock)
719
0
{
720
#if PJ_GRP_LOCK_DEBUG
721
    grp_lock_ref *ref;
722
    char info_buf[1000];
723
    pj_str_t info;
724
725
    info.ptr = info_buf;
726
    info.slen = 0;
727
728
    grp_lock_add_ref(grp_lock);
729
    pj_enter_critical_section();
730
731
    ref = grp_lock->ref_list.next;
732
    while (ref != &grp_lock->ref_list && info.slen < sizeof(info_buf)) {
733
        char *start = info.ptr + info.slen;
734
        int max_len = sizeof(info_buf) - info.slen;
735
        int len;
736
737
        len = pj_ansi_snprintf(start, max_len, "\t%s:%d\n", ref->file, ref->line);
738
        if (len < 1 || len >= max_len) {
739
            len = strlen(ref->file);
740
            if (len > max_len - 1)
741
                len = max_len - 1;
742
743
            memcpy(start, ref->file, len);
744
            start[len++] = '\n';
745
        }
746
747
        info.slen += len;
748
749
        ref = ref->next;
750
    }
751
752
    if (ref != &grp_lock->ref_list) {
753
        int i;
754
        for (i=0; i<4; ++i)
755
            info_buf[sizeof(info_buf)-i-1] = '.';
756
    }
757
    info.ptr[info.slen-1] = '\0';
758
759
    pj_leave_critical_section();
760
761
    PJ_LOG(4,(THIS_FILE, "Group lock %p, ref_cnt=%d. Reference holders:\n%s",
762
               grp_lock, pj_grp_lock_get_ref(grp_lock)-1, info.ptr));
763
764
    grp_lock_dec_ref(grp_lock);
765
#else
766
0
    PJ_LOG(4,(THIS_FILE, "Group lock %p, ref_cnt=%d.",
767
0
               grp_lock, pj_grp_lock_get_ref(grp_lock)));
768
0
#endif
769
0
}