Module: sip-router Branch: master Commit: c9957ad5bf1b672a2661f4faeb9b781820a1f636 URL: http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=c9957ad5...
Author: Alex Balashov abalashov@evaristesys.com Committer: Alex Balashov abalashov@evaristesys.com Date: Fri Dec 21 21:08:39 2012 -0500
dialog(k): Reworked dlg_set_timeout_by_profile() code to change dialog timeouts outside of a profile lock.
This is in order to avoid deadlock complications arising from lock/ref count operations upstream. It appears that when update_dlg_timer() fails, it does not relinquish control back to the calling function, which created problems with unreleased profile locks.
---
modules_k/dialog/dlg_profile.c | 78 ++++++++++++++++++++++++++++++++++------ 1 files changed, 67 insertions(+), 11 deletions(-)
diff --git a/modules_k/dialog/dlg_profile.c b/modules_k/dialog/dlg_profile.c index 41802ea..5b388d8 100644 --- a/modules_k/dialog/dlg_profile.c +++ b/modules_k/dialog/dlg_profile.c @@ -714,9 +714,23 @@ int is_known_dlg(struct sip_msg *msg) { int dlg_set_timeout_by_profile(struct dlg_profile_table *profile, str *value, int timeout) { - unsigned int i; + unsigned int i = 0; + dlg_cell_t *this_dlg = NULL; struct dlg_profile_hash *ph = NULL;
+ /* Private structure necessary for manipulating dialog + * timeouts outside of profile locks. Admittedly, an + * ugly hack, but avoids some concurrency issues. + */ + + struct dlg_map_list { + unsigned int h_id; + unsigned int h_entry; + struct dlg_map_list *next; + } *map_head, *map_scan, *map_scan_next; + + map_head = NULL; + /* If the profile has no value, iterate through every * node and set its timeout. */ @@ -730,11 +744,23 @@ int dlg_set_timeout_by_profile(struct dlg_profile_table *profile, if(!ph) continue; do { - if(update_dlg_timeout(ph->dlg, timeout) < 0) { - lock_release(&profile->lock); + struct dlg_map_list *d = malloc(sizeof(struct dlg_map_list)); + + if(!d) return -1; - }
+ memset(d, 0, sizeof(struct dlg_map_list)); + + d->h_id = ph->dlg->h_id; + d->h_entry = ph->dlg->h_entry; + + if(map_head == NULL) + map_head = d; + else { + d->next = map_head; + map_head = d; + } + ph = ph->next; } while(ph != profile->entries[i].first); } @@ -750,15 +776,24 @@ int dlg_set_timeout_by_profile(struct dlg_profile_table *profile, ph = profile->entries[i].first;
if(ph) { - do { - if(value->len == ph->value.len && - memcmp(value->s, ph->value.s, - value->len) == 0) { + do { + if(ph && value->len == ph->value.len && + memcmp(value->s, ph->value.s, value->len) == 0) { + struct dlg_map_list *d = malloc(sizeof(struct dlg_map_list));
- if(update_dlg_timeout(ph->dlg, - timeout) < 0) { - lock_release(&profile->lock); + if(!d) return -1; + + memset(d, 0, sizeof(struct dlg_map_list)); + + d->h_id = ph->dlg->h_id; + d->h_entry = ph->dlg->h_entry; + + if(map_head == NULL) + map_head = d; + else { + d->next = map_head; + map_head = d; } }
@@ -769,6 +804,27 @@ int dlg_set_timeout_by_profile(struct dlg_profile_table *profile, lock_release(&profile->lock); }
+ /* Walk the list and bulk-set the timeout */ + + for(map_scan = map_head; map_scan != NULL; map_scan = map_scan_next) { + map_scan_next = map_scan->next; + + this_dlg = dlg_lookup(map_scan->h_entry, map_scan->h_id); + + if(!this_dlg) { + LM_CRIT("Unable to find dialog %d:%d\n", map_scan->h_entry, map_scan->h_id); + } else if(this_dlg->state >= DLG_STATE_EARLY) { + if(update_dlg_timeout(this_dlg, timeout) < 0) { + LM_ERR("Unable to set timeout on %d:%d\n", map_scan->h_entry, + map_scan->h_id); + } + + dlg_release(this_dlg); + } + + free(map_scan); + } + return 0; }