Ignore:
Timestamp:
Sep 19, 2006 1:37:53 PM (18 years ago)
Author:
bennylp
Message:

Fixed race-condition/deadlock problems in the dialog/user agent layer
all the way up to PJSUA-API:

  • standardized locking order: dialog then user agent, and dialog then PJSUA
  • any threads that attempt to acquire mutexes in different order than above MUST employ retry mechanism (for an example, see acquire_call() in pjsua_call.c). This retry mechanism has also been used in the UA layer (sip_ua_layer.c) since it needs to lock user agent layer first before the dialog.
  • introduced pjsip_dlg_try_inc_lock() and PJSUA_TRY_LOCK() to accomodate above.
  • pjsua tested on Quad Xeon with 4 threads and 200 cps, so far so good.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c

    r719 r729  
    384384        return PJ_FALSE; 
    385385 
     386    PJSUA_LOCK(); 
    386387 
    387388    /* Verify that we can handle the request. */ 
     
    408409        } 
    409410 
     411        PJSUA_UNLOCK(); 
    410412        return PJ_TRUE; 
    411413    }  
     
    428430        PJ_LOG(2,(THIS_FILE,  
    429431                  "Unable to accept incoming call (too many calls)")); 
     432        PJSUA_UNLOCK(); 
    430433        return PJ_TRUE; 
    431434    } 
     
    446449        pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, 
    447450                                      NULL, NULL); 
     451        PJSUA_UNLOCK(); 
    448452        return PJ_TRUE; 
    449453    } 
     
    463467        pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, 
    464468                                      NULL, NULL); 
     469        PJSUA_UNLOCK(); 
    465470        return PJ_TRUE; 
    466471    } 
     
    472477        pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, 
    473478                                      NULL, NULL); 
    474  
     479        PJSUA_UNLOCK(); 
    475480        return PJ_TRUE; 
    476481    } 
     
    500505        pjsip_dlg_terminate(dlg); 
    501506         */ 
     507        PJSUA_UNLOCK(); 
    502508        return PJ_TRUE; 
    503509    } 
     
    521527        pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL); 
    522528        pjsip_inv_terminate(inv, 500, PJ_FALSE); 
     529        PJSUA_UNLOCK(); 
    523530        return PJ_TRUE; 
    524531 
     
    537544 
    538545    /* This INVITE request has been handled. */ 
     546    PJSUA_UNLOCK(); 
    539547    return PJ_TRUE; 
    540548} 
     
    566574 
    567575 
     576/* Acquire lock to the specified call_id */ 
     577static pj_status_t acquire_call(const char *title, 
     578                                pjsua_call_id call_id, 
     579                                pjsua_call **p_call) 
     580{ 
     581    enum { MAX_RETRY=50 }; 
     582    unsigned retry; 
     583    pjsua_call *call; 
     584    pj_bool_t has_pjsua_lock; 
     585    pj_status_t status; 
     586 
     587    for (retry=0; retry<MAX_RETRY; ++retry) { 
     588         
     589        has_pjsua_lock = PJ_FALSE; 
     590 
     591        status = PJSUA_TRY_LOCK(); 
     592        if (status != PJ_SUCCESS) { 
     593            pj_thread_sleep(retry/10); 
     594            continue; 
     595        } 
     596 
     597        has_pjsua_lock = PJ_TRUE; 
     598        call = &pjsua_var.calls[call_id]; 
     599 
     600        if (call->inv == NULL) { 
     601            PJSUA_UNLOCK(); 
     602            PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title)); 
     603            return PJSIP_ESESSIONTERMINATED; 
     604        } 
     605 
     606        status = pjsip_dlg_try_inc_lock(call->inv->dlg); 
     607        if (status != PJ_SUCCESS) { 
     608            PJSUA_UNLOCK(); 
     609            pj_thread_sleep(retry/10); 
     610            continue; 
     611        } 
     612 
     613        PJSUA_UNLOCK(); 
     614 
     615        break; 
     616    } 
     617 
     618    if (status != PJ_SUCCESS) { 
     619        if (has_pjsua_lock == PJ_FALSE) 
     620            PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex " 
     621                                 "(possibly system has deadlocked) in %s", 
     622                                 title)); 
     623        else 
     624            PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex " 
     625                                 "(possibly system has deadlocked) in %s", 
     626                                 title)); 
     627        return PJ_ETIMEDOUT; 
     628    } 
     629     
     630    *p_call = call; 
     631 
     632    return PJ_SUCCESS; 
     633} 
     634 
     635 
    568636/* 
    569637 * Get the conference port identification associated with the call. 
     
    571639PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id) 
    572640{ 
     641    pjsua_call *call; 
     642    pjsua_conf_port_id port_id; 
     643    pj_status_t status; 
     644 
    573645    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,  
    574646                     PJ_EINVAL); 
    575     return pjsua_var.calls[call_id].conf_slot; 
    576 } 
     647 
     648    status = acquire_call("pjsua_call_get_conf_port()", call_id, &call); 
     649    if (status != PJ_SUCCESS) 
     650        return -1; 
     651 
     652    port_id = call->conf_slot; 
     653 
     654    pjsip_dlg_dec_lock(call->inv->dlg); 
     655 
     656    return port_id; 
     657} 
     658 
    577659 
    578660 
     
    584666{ 
    585667    pjsua_call *call; 
     668    pj_status_t status; 
    586669 
    587670    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
     
    590673    pj_bzero(info, sizeof(*info)); 
    591674 
    592     PJSUA_LOCK(); 
    593  
    594     call = &pjsua_var.calls[call_id]; 
    595  
    596     if (call->inv == NULL) { 
    597         PJSUA_UNLOCK(); 
    598         return PJ_SUCCESS; 
    599     } 
    600  
    601     pjsip_dlg_inc_lock(call->inv->dlg); 
    602  
     675    status = acquire_call("pjsua_call_get_info()", call_id, &call); 
     676    if (status != PJ_SUCCESS) { 
     677        return status; 
     678    } 
    603679 
    604680    /* id and role */ 
     
    697773 
    698774    pjsip_dlg_dec_lock(call->inv->dlg); 
    699     PJSUA_UNLOCK(); 
    700775 
    701776    return PJ_SUCCESS; 
     
    743818                     PJ_EINVAL); 
    744819 
    745     PJSUA_LOCK(); 
    746  
    747     call = &pjsua_var.calls[call_id]; 
    748  
    749     if (call->inv == NULL) { 
    750         PJ_LOG(3,(THIS_FILE, "Call %d already disconnected", call_id)); 
    751         PJSUA_UNLOCK(); 
    752         return PJSIP_ESESSIONTERMINATED; 
    753     } 
     820    status = acquire_call("pjsua_call_answer()", call_id, &call); 
     821    if (status != PJ_SUCCESS) 
     822        return status; 
    754823 
    755824    if (call->res_time.sec == 0) 
     
    761830        pjsua_perror(THIS_FILE, "Error creating response",  
    762831                     status); 
    763         PJSUA_UNLOCK(); 
     832        pjsip_dlg_dec_lock(call->inv->dlg); 
    764833        return status; 
    765834    } 
     
    774843                     status); 
    775844 
    776     PJSUA_UNLOCK(); 
     845    pjsip_dlg_dec_lock(call->inv->dlg); 
    777846 
    778847    return status; 
     
    794863 
    795864 
     865    if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) { 
     866        PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d", 
     867                             call_id)); 
     868    } 
     869     
    796870    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
    797871                     PJ_EINVAL); 
    798872 
    799     PJSUA_LOCK(); 
    800  
    801     call = &pjsua_var.calls[call_id]; 
    802  
    803     if (!call->inv) { 
    804         PJ_LOG(3,(THIS_FILE,"Invalid call or call has been disconnected")); 
    805         PJSUA_UNLOCK(); 
    806         return PJ_EINVAL; 
    807     } 
     873    status = acquire_call("pjsua_call_hangup()", call_id, &call); 
     874    if (status != PJ_SUCCESS) 
     875        return status; 
    808876 
    809877    if (code==0) { 
     
    821889                     "Failed to create end session message",  
    822890                     status); 
    823         PJSUA_UNLOCK(); 
     891        pjsip_dlg_dec_lock(call->inv->dlg); 
    824892        return status; 
    825893    } 
     
    830898     */ 
    831899    if (tdata == NULL) { 
    832         PJSUA_UNLOCK(); 
     900        pjsip_dlg_dec_lock(call->inv->dlg); 
    833901        return PJ_SUCCESS; 
    834902    } 
     
    843911                     "Failed to send end session message",  
    844912                     status); 
    845         PJSUA_UNLOCK(); 
     913        pjsip_dlg_dec_lock(call->inv->dlg); 
    846914        return status; 
    847915    } 
    848916 
    849     PJSUA_UNLOCK(); 
     917    pjsip_dlg_dec_lock(call->inv->dlg); 
    850918 
    851919    return PJ_SUCCESS; 
     
    867935                     PJ_EINVAL); 
    868936 
    869     PJSUA_LOCK(); 
    870  
    871     call = &pjsua_var.calls[call_id]; 
    872      
    873     if (!call->inv) { 
    874         PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 
    875         PJSUA_UNLOCK(); 
    876         return PJSIP_ESESSIONTERMINATED; 
    877     } 
     937    status = acquire_call("pjsua_call_set_hold()", call_id, &call); 
     938    if (status != PJ_SUCCESS) 
     939        return status; 
     940 
    878941 
    879942    if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) { 
    880943        PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed")); 
    881         PJSUA_UNLOCK(); 
     944        pjsip_dlg_dec_lock(call->inv->dlg); 
    882945        return PJSIP_ESESSIONSTATE; 
    883946    } 
     
    885948    status = create_inactive_sdp(call, &sdp); 
    886949    if (status != PJ_SUCCESS) { 
    887         PJSUA_UNLOCK(); 
     950        pjsip_dlg_dec_lock(call->inv->dlg); 
    888951        return status; 
    889952    } 
     
    893956    if (status != PJ_SUCCESS) { 
    894957        pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status); 
    895         PJSUA_UNLOCK(); 
     958        pjsip_dlg_dec_lock(call->inv->dlg); 
    896959        return status; 
    897960    } 
     
    904967    if (status != PJ_SUCCESS) { 
    905968        pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); 
    906         PJSUA_UNLOCK(); 
     969        pjsip_dlg_dec_lock(call->inv->dlg); 
    907970        return status; 
    908971    } 
    909972 
    910     PJSUA_UNLOCK(); 
     973    pjsip_dlg_dec_lock(call->inv->dlg); 
    911974 
    912975    return PJ_SUCCESS; 
     
    930993                     PJ_EINVAL); 
    931994 
    932     PJSUA_LOCK(); 
    933  
    934     call = &pjsua_var.calls[call_id]; 
    935  
    936     if (!call->inv) { 
    937         PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 
    938         PJSUA_UNLOCK(); 
    939         return PJSIP_ESESSIONTERMINATED; 
    940     } 
    941  
     995    status = acquire_call("pjsua_call_reinvite()", call_id, &call); 
     996    if (status != PJ_SUCCESS) 
     997        return status; 
    942998 
    943999    if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) { 
    9441000        PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed")); 
    945         PJSUA_UNLOCK(); 
     1001        pjsip_dlg_dec_lock(call->inv->dlg); 
    9461002        return PJSIP_ESESSIONSTATE; 
    9471003    } 
     
    9551011        pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",  
    9561012                     status); 
    957         PJSUA_UNLOCK(); 
     1013        pjsip_dlg_dec_lock(call->inv->dlg); 
    9581014        return status; 
    9591015    } 
     
    9631019    if (status != PJ_SUCCESS) { 
    9641020        pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status); 
    965         PJSUA_UNLOCK(); 
     1021        pjsip_dlg_dec_lock(call->inv->dlg); 
    9661022        return status; 
    9671023    } 
     
    9741030    if (status != PJ_SUCCESS) { 
    9751031        pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); 
    976         PJSUA_UNLOCK(); 
     1032        pjsip_dlg_dec_lock(call->inv->dlg); 
    9771033        return status; 
    9781034    } 
    9791035 
    980     PJSUA_UNLOCK(); 
     1036    pjsip_dlg_dec_lock(call->inv->dlg); 
    9811037 
    9821038    return PJ_SUCCESS; 
     
    10001056                     PJ_EINVAL); 
    10011057     
    1002     PJSUA_LOCK(); 
    1003  
    1004     call = &pjsua_var.calls[call_id]; 
    1005  
    1006     if (!call->inv) { 
    1007         PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 
    1008         PJSUA_UNLOCK(); 
    1009         return PJSIP_ESESSIONTERMINATED; 
    1010     } 
     1058    pjsip_dlg_dec_lock(call->inv->dlg); 
     1059    status = acquire_call("pjsua_call_xfer()", call_id, &call); 
     1060    if (status != PJ_SUCCESS) 
     1061        return status; 
     1062 
    10111063    
    10121064    /* Create xfer client subscription. 
     
    10171069    if (status != PJ_SUCCESS) { 
    10181070        pjsua_perror(THIS_FILE, "Unable to create xfer", status); 
    1019         PJSUA_UNLOCK(); 
     1071        pjsip_dlg_dec_lock(call->inv->dlg); 
    10201072        return status; 
    10211073    } 
     
    10271079    if (status != PJ_SUCCESS) { 
    10281080        pjsua_perror(THIS_FILE, "Unable to create REFER request", status); 
    1029         PJSUA_UNLOCK(); 
     1081        pjsip_dlg_dec_lock(call->inv->dlg); 
    10301082        return status; 
    10311083    } 
     
    10381090    if (status != PJ_SUCCESS) { 
    10391091        pjsua_perror(THIS_FILE, "Unable to send REFER request", status); 
    1040         PJSUA_UNLOCK(); 
     1092        pjsip_dlg_dec_lock(call->inv->dlg); 
    10411093        return status; 
    10421094    } 
     
    10471099     */ 
    10481100 
    1049     PJSUA_UNLOCK(); 
     1101    pjsip_dlg_dec_lock(call->inv->dlg); 
    10501102 
    10511103    return PJ_SUCCESS; 
     
    10661118                     PJ_EINVAL); 
    10671119     
    1068     PJSUA_LOCK(); 
     1120    status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call); 
     1121    if (status != PJ_SUCCESS) 
     1122        return status; 
    10691123 
    10701124    call = &pjsua_var.calls[call_id]; 
     
    10721126    if (!call->session) { 
    10731127        PJ_LOG(3,(THIS_FILE, "Media is not established yet!")); 
    1074         PJSUA_UNLOCK(); 
     1128        pjsip_dlg_dec_lock(call->inv->dlg); 
    10751129        return PJ_EINVALIDOP; 
    10761130    } 
     
    10781132    status = pjmedia_session_dial_dtmf( call->session, 0, digits); 
    10791133 
    1080     PJSUA_UNLOCK(); 
     1134    pjsip_dlg_dec_lock(call->inv->dlg); 
    10811135 
    10821136    return status; 
     
    11041158                     PJ_EINVAL); 
    11051159 
    1106     PJSUA_LOCK(); 
    1107  
    1108     call = &pjsua_var.calls[call_id]; 
    1109  
    1110     if (!call->inv) { 
    1111         PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 
    1112         PJSUA_UNLOCK(); 
    1113         return PJSIP_ESESSIONTERMINATED; 
    1114     } 
    1115  
    1116     /* Lock dialog. */ 
    1117     pjsip_dlg_inc_lock(call->inv->dlg); 
    1118  
     1160    status = acquire_call("pjsua_call_send_im", call_id, &call); 
     1161    if (status != PJ_SUCCESS) 
     1162        return status; 
     1163     
    11191164    /* Set default media type if none is specified */ 
    11201165    if (mime_type == NULL) { 
     
    11681213on_return: 
    11691214    pjsip_dlg_dec_lock(call->inv->dlg); 
    1170     PJSUA_UNLOCK(); 
    11711215    return status; 
    11721216} 
     
    11841228    pj_status_t status; 
    11851229 
    1186  
    11871230    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
    11881231                     PJ_EINVAL); 
    11891232 
    1190     PJSUA_LOCK(); 
    1191  
    1192     call = &pjsua_var.calls[call_id]; 
    1193  
    1194     if (!call->inv) { 
    1195         PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); 
    1196         PJSUA_UNLOCK(); 
    1197         return PJSIP_ESESSIONTERMINATED; 
    1198     } 
    1199  
    1200     /* Lock dialog. */ 
    1201     pjsip_dlg_inc_lock(call->inv->dlg); 
    1202      
     1233    status = acquire_call("pjsua_call_send_typing_ind", call_id, &call); 
     1234    if (status != PJ_SUCCESS) 
     1235        return status; 
     1236 
    12031237    /* Create request message. */ 
    12041238    status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method, 
     
    12251259on_return: 
    12261260    pjsip_dlg_dec_lock(call->inv->dlg); 
    1227     PJSUA_UNLOCK(); 
    12281261    return status; 
    12291262} 
     
    15021535    char tmp[128]; 
    15031536    char *p, *end; 
     1537    pj_status_t status; 
    15041538    int len; 
    15051539 
     
    15071541                     PJ_EINVAL); 
    15081542 
    1509     PJSUA_LOCK(); 
    1510  
    1511     call = &pjsua_var.calls[call_id]; 
     1543    status = acquire_call("pjsua_call_dump()", call_id, &call); 
     1544    if (status != PJ_SUCCESS) 
     1545        return status; 
    15121546 
    15131547    *buffer = '\0'; 
     
    15151549    end = buffer + maxlen; 
    15161550    len = 0; 
    1517  
    1518     if (call->inv == NULL) { 
    1519         PJSUA_UNLOCK(); 
    1520         return PJ_EINVALIDOP; 
    1521     } 
    15221551 
    15231552    print_call(indent, call_id, tmp, sizeof(tmp)); 
     
    15701599        dump_media_session(indent, p, end-p, call->session); 
    15711600 
    1572     PJSUA_UNLOCK(); 
     1601    pjsip_dlg_dec_lock(call->inv->dlg); 
    15731602 
    15741603    return PJ_SUCCESS; 
Note: See TracChangeset for help on using the changeset viewer.