From 84e71c86b3d86491924368fbab194decdabd76a9 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Thu, 19 Dec 2024 15:12:14 -0400 Subject: [PATCH] MT#61368 add call_get2() This function retrieves two calls from the global call hash while avoiding deadlocks. This is needed because a "call_get(A) + call_get(B)" would deadlock against a concurrent "call_get(B) + call_get(A)" Unused at this point. Change-Id: I95127ce1afa19386a847984ca26eb7d7368e6569 --- daemon/call.c | 40 ++++++++++++++++++++++++++++++++++++++++ include/call.h | 3 +++ 2 files changed, 43 insertions(+) diff --git a/daemon/call.c b/daemon/call.c index 6410f555d..580a67b18 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -4347,6 +4347,46 @@ call_t *call_get(const str *callid) { return ret; } +// special version of call_get() to get two calls while avoiding deadlock +call_get2_ret_t call_get2(call_t **ret1, call_t **ret2, const str *callid1, const str *callid2) { + call_get2_ret_t ret; + + while (true) { + RWLOCK_R(&rtpe_callhash_lock); + + *ret1 = t_hash_table_lookup(rtpe_callhash, callid1); + if (!*ret1) + return CG2_NF1; + *ret2 = t_hash_table_lookup(rtpe_callhash, callid2); + if (!*ret2) + return CG2_NF2; + + if (*ret1 == *ret2) { + *ret2 = NULL; + ret = CG2_SAME; + rwlock_lock_w(&(*ret1)->master_lock); + obj_hold(*ret1); + } + else { + rwlock_lock_w(&(*ret1)->master_lock); + if (rwlock_trylock_w(&(*ret2)->master_lock)) { + // try again + rwlock_unlock_w(&(*ret1)->master_lock); + continue; + } + + ret = CG2_OK; + obj_hold(*ret1); + obj_hold(*ret2); + } + + break; + } + + log_info_call(*ret1); + return ret; +} + static gboolean fragment_move(str *key, fragment_q *q, void *c) { call_t *call = c; t_hash_table_insert(call->sdp_fragments, key, q); diff --git a/include/call.h b/include/call.h index a3821b134..ead6cc2d8 100644 --- a/include/call.h +++ b/include/call.h @@ -833,6 +833,9 @@ struct call_monologue *call_get_monologue(call_t *call, const str *fromtag); struct call_monologue *call_get_or_create_monologue(call_t *call, const str *fromtag); __attribute__((nonnull(1))) call_t *call_get(const str *callid); +typedef enum { CG2_OK, CG2_NF1, CG2_NF2, CG2_SAME } call_get2_ret_t; +__attribute__((nonnull(1, 2, 3, 4))) +call_get2_ret_t call_get2(call_t **, call_t **, const str *, const str *); __attribute__((nonnull(1, 2))) bool call_merge(call_t *, call_t **); __attribute__((nonnull(2, 3)))