void SipTransactionList::markAvailable(SipTransaction& transaction) { lock(); if(!transaction.isBusy()) { UtlString transactionString; transaction.toString(transactionString, FALSE); OsSysLog::add(FAC_SIP, PRI_ERR, "SipTransactionList::markAvailable transaction not locked: %s\n", transactionString.data()); } else { transaction.markAvailable(); } unlock(); }
void SipTransactionList::markAvailable(SipTransaction& transaction) { lock(); if(!transaction.isBusy()) { UtlString transactionString; transaction.toString(transactionString, FALSE); Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipTransactionList::markAvailable" " transaction not locked: %s", transactionString.data()); } else { transaction.markAvailable(); } unlock(); }
//: Find a transaction for the given message SipTransaction* SipTransactionList::findTransactionFor(const SipMessage& message, UtlBoolean isOutgoing, enum SipTransaction::messageRelationship& relationship) { SipTransaction* transactionFound = NULL; UtlString callId; SipTransaction::buildHash(message, isOutgoing, callId); lock(); // See if the message knows its transaction // DO NOT TOUCH THE CONTENTS of this transaction as it may no // longer exist. It can only be used as an ID for the transaction. SipTransaction* messageTransaction = message.getSipTransaction(); UtlString matchTransaction(callId); UtlHashBagIterator iterator(mTransactions, &matchTransaction); relationship = SipTransaction::MESSAGE_UNKNOWN; while ((transactionFound = (SipTransaction*) iterator())) { // If the message knows its SIP transaction // and the found transaction pointer does not match, skip the // expensive relationship calculation // The messageTransaction MUST BE TREATED AS OPAQUE // as it may have been deleted. if( messageTransaction && transactionFound != messageTransaction ) { continue; } // If the transaction has never sent the original rquest // it should never get a match for any messages. if( messageTransaction == NULL // this message does not point to this TX && ((transactionFound->getState()) == SipTransaction::TRANSACTION_LOCALLY_INIITATED) ) { continue; } relationship = transactionFound->whatRelation(message, isOutgoing); if(relationship == SipTransaction::MESSAGE_REQUEST || relationship == SipTransaction::MESSAGE_PROVISIONAL || relationship == SipTransaction::MESSAGE_FINAL || relationship == SipTransaction::MESSAGE_NEW_FINAL || relationship == SipTransaction::MESSAGE_CANCEL || relationship == SipTransaction::MESSAGE_CANCEL_RESPONSE || relationship == SipTransaction::MESSAGE_ACK || relationship == SipTransaction::MESSAGE_2XX_ACK || relationship == SipTransaction::MESSAGE_DUPLICATE) { break; } } UtlBoolean isBusy = FALSE; if(transactionFound == NULL) { relationship = SipTransaction::MESSAGE_UNKNOWN; } else { isBusy = transactionFound->isBusy(); if(!isBusy) { transactionFound->markBusy(); } } unlock(); if(transactionFound && isBusy) { // If we cannot lock it, it does not exist if(!waitUntilAvailable(transactionFound, callId)) { if (OsSysLog::willLog(FAC_SIP, PRI_WARNING)) { UtlString relationString; SipTransaction::getRelationshipString(relationship, relationString); OsSysLog::add(FAC_SIP, PRI_WARNING, "SipTransactionList::findTransactionFor %p not available relation: %s", transactionFound, relationString.data()); } transactionFound = NULL; } } #if 0 // enable only for transaction match debugging - log is confusing otherwise UtlString relationString; SipTransaction::getRelationshipString(relationship, relationString); UtlString bytes; int len; message.getBytes(&bytes, &len); OsSysLog::add(FAC_SIP, PRI_DEBUG ,"SipTransactionList::findTransactionFor %p %s %s %s" # ifdef TIME_LOG "\n\tTime Log %s" # endif ,&message ,isOutgoing ? "OUTGOING" : "INCOMING" ,transactionFound ? "FOUND" : "NOT FOUND" ,relationString.data() # ifdef TIME_LOG ,findTimeLog.data() # endif ); #endif return(transactionFound); }
void SipTransactionList::removeOldTransactions(long oldTransaction, long oldInviteTransaction) { SipTransaction** transactionsToBeDeleted = NULL; int deleteCount = 0; int busyCount = 0; # ifdef TIME_LOG OsTimeLog gcTimes; gcTimes.addEvent("start"); # endif lock(); int numTransactions = mTransactions.entries(); if(numTransactions > 0) { UtlHashBagIterator iterator(mTransactions); SipTransaction* transactionFound = NULL; long transTime; // Pull all of the transactions to be deleted out of the list while((transactionFound = (SipTransaction*) iterator())) { if(transactionFound->isBusy()) busyCount++; transTime = transactionFound->getTimeStamp(); // Invites need to be kept longer than other transactions if(((!transactionFound->isMethod(SIP_INVITE_METHOD) && transTime < oldTransaction) || transTime < oldInviteTransaction) && ! transactionFound->isBusy()) { // Remove it from the list mTransactions.removeReference(transactionFound); OsSysLog::add(FAC_SIP, PRI_DEBUG, "removing transaction %p\n",transactionFound); // Make sure we have a pointer array to hold it if(transactionsToBeDeleted == NULL) { transactionsToBeDeleted = new SipTransaction*[numTransactions]; } // Put it in the pointer array transactionsToBeDeleted[deleteCount] = transactionFound; deleteCount++; // Make sure the events waiting for the transaction // to be available are signaled before we delete // any of the transactions or we end up with // incomplete transaction trees (i.e. deleted branches) transactionFound->signalAllAvailable(); transactionFound = NULL; } } } unlock(); // We do not need the lock if the transactions have been // removed from the list if ( deleteCount || busyCount ) // do not log 'doing nothing when nothing to do', even at debug level { OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipTransactionList::removeOldTransactions deleting %d of %d transactions (%d busy)\n", deleteCount , numTransactions, busyCount); } // Delete the transactions in the array if (transactionsToBeDeleted) { # ifdef TIME_LOG gcTimes.addEvent("start delete"); # endif for(int txIndex = 0; txIndex < deleteCount; txIndex++) { delete transactionsToBeDeleted[txIndex]; # ifdef TIME_LOG gcTimes.addEvent("transaction deleted"); # endif } # ifdef TIME_LOG gcTimes.addEvent("finish delete"); # endif /* while((transactionFound = (SipTransaction*) iterator())) { transactionFound->stopTimers(); } */ delete[] transactionsToBeDeleted; transactionsToBeDeleted = NULL; } # ifdef TIME_LOG UtlString timeString; gcTimes.getLogString(timeString); OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipTransactionList::removeOldTransactions " "%s", timeString.data() ); # endif }
//: Find a transaction for the given message SipTransaction* SipTransactionList::findTransactionFor(const SipMessage& message, UtlBoolean isOutgoing, enum SipTransaction::messageRelationship& relationship) { SipTransaction* transactionFound = NULL; SipTransaction* transaction2xxFound = NULL; enum SipTransaction::messageRelationship relationship2xx = SipTransaction::MESSAGE_UNKNOWN; UtlString callId; SipTransaction::buildHash(message, isOutgoing, callId); // // Call garbage collection before we further process existence of a transaction. // mpSipUserAgent->garbageCollection(); lock(); // See if the message knows its transaction // DO NOT TOUCH THE CONTENTS of this transaction as it may no // longer exist. It can only be used as an ID for the transaction. SipTransaction* messageTransaction = message.getSipTransaction(); UtlString matchTransaction(callId); UtlHashBagIterator iterator(mTransactions, &matchTransaction); relationship = SipTransaction::MESSAGE_UNKNOWN; # ifdef TIME_LOG OsTimeLog findTimes; findTimes.addEvent("start"); # endif while ((transactionFound = (SipTransaction*) iterator())) { // If the message knows its SIP transaction // and the found transaction pointer does not match, skip the // expensive relationship calculation // The messageTransaction MUST BE TREATED AS OPAQUE // as it may have been deleted. if( messageTransaction && transactionFound != messageTransaction ) { # ifdef TIME_LOG findTimes.addEvent("mismatch"); # endif continue; } // If the transaction has never sent the original rquest // it should never get a match for any messages. if( messageTransaction == NULL // this message does not point to this TX && ((transactionFound->getState()) == SipTransaction::TRANSACTION_LOCALLY_INIITATED) ) { # ifdef TIME_LOG findTimes.addEvent("unsent"); # endif continue; } relationship = transactionFound->whatRelation(message, isOutgoing); // Since 2xx ACK transactions always get a new Via branch, we have to // make sure there isn't another error transaction with a better match (branch included) // // NOTE: Adding this code makes obvious that ACK's for 2xx responses always(??? at least on initial invite) // match more than one transaction tree, the original tx tree in the forking proxy function and the // consequent tx tree in the auth proxy function. This is a result of the fix for XECS-414, which // allows matching a transaction for a 2xx ACK without matching branch id's. // This 2xx-ACK-match-except-branch result combined with the failuer to find a complete 2xx-ACK_match-with-branch, // means we can be assured that this ACK should be sent upstream. The ACK is then treated as a new transaction // and the EXISTENCE of the matching transaction is used to navigate through the code. // The CONTENTS of the matched transaction are not important except for the matching itself. // ACKs for error responses will always match ONLY one transaction, since the branch id must also match. if(relationship == SipTransaction::MESSAGE_2XX_ACK || relationship == SipTransaction::MESSAGE_2XX_ACK_PROXY ) { if (transaction2xxFound) { UtlString bytes; ssize_t len; message.getBytes(&bytes, &len); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG ,"SipTransactionList::findTransactionFor" " more than one 2xx match for message %p(%p) %s " " previous match (%p) %s" " current match (%p) %s" ,&message ,messageTransaction ,isOutgoing ? "OUTGOING" : "INCOMING" ,transaction2xxFound ,SipTransaction::relationshipString(relationship2xx) ,transactionFound ,SipTransaction::relationshipString(relationship) ); relationship2xx = relationship; transaction2xxFound = transactionFound; } else { relationship2xx = relationship; transaction2xxFound = transactionFound; UtlString bytes; ssize_t len; message.getBytes(&bytes, &len); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG ,"SipTransactionList::findTransactionFor" " 2xx match for message %p(%p) %s " " current match (%p) %s" ,&message ,messageTransaction ,isOutgoing ? "OUTGOING" : "INCOMING" ,transactionFound ,SipTransaction::relationshipString(relationship) ); } continue; } if(relationship == SipTransaction::MESSAGE_ACK ) { UtlString bytes; ssize_t len; message.getBytes(&bytes, &len); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG ,"SipTransactionList::findTransactionFor" " ACK match for message %p(%p) %s " " current match (%p) %s" " previous match (%p) %s" ,&message ,messageTransaction ,isOutgoing ? "OUTGOING" : "INCOMING" ,transactionFound ,SipTransaction::relationshipString(relationship) ,transaction2xxFound ,SipTransaction::relationshipString(relationship2xx) ); } if(relationship == SipTransaction::MESSAGE_REQUEST || relationship == SipTransaction::MESSAGE_PROVISIONAL || relationship == SipTransaction::MESSAGE_FINAL || relationship == SipTransaction::MESSAGE_NEW_FINAL || relationship == SipTransaction::MESSAGE_CANCEL || relationship == SipTransaction::MESSAGE_CANCEL_RESPONSE || relationship == SipTransaction::MESSAGE_ACK || relationship == SipTransaction::MESSAGE_DUPLICATE) { break; } } // if((transactionFound == NULL) && transaction2xxFound) { relationship = relationship2xx; transactionFound = transaction2xxFound; } # ifdef TIME_LOG if ( transactionFound ) { findTimes.addEvent("found"); } else { findTimes.addEvent("unfound"); } # endif UtlBoolean isBusy = FALSE; if(transactionFound == NULL) { relationship = SipTransaction::MESSAGE_UNKNOWN; } else { isBusy = transactionFound->isBusy(); if(!isBusy) { transactionFound->markBusy(); } } unlock(); if(transactionFound && isBusy) { # ifdef TIME_LOG findTimes.addEvent("checking"); # endif // If we cannot lock it, it does not exist if(!waitUntilAvailable(transactionFound, callId)) { if (Os::Logger::instance().willLog(FAC_SIP, PRI_WARNING)) { Os::Logger::instance().log(FAC_SIP, PRI_WARNING, "SipTransactionList::findTransactionFor" " %p not available relation: %s", transactionFound, SipTransaction::relationshipString(relationship)); } # ifdef TIME_LOG findTimes.addEvent("notavail"); # endif transactionFound = NULL; } } # ifdef TIME_LOG findTimes.addEvent("done"); UtlString findTimeLog; findTimes.getLogString(findTimeLog); # endif #ifdef TRANSACTION_MATCH_DEBUG UtlString bytes; ssize_t len; message.getBytes(&bytes, &len); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG ,"SipTransactionList::findTransactionFor %p(%p) %s %s(%p) %s" # ifdef TIME_LOG "\n\tTime Log %s" # endif ,&message ,messageTransaction ,isOutgoing ? "OUTGOING" : "INCOMING" ,transactionFound ? "FOUND" : "NOT FOUND" ,transactionFound ,SipTransaction::relationshipString(relationship) # ifdef TIME_LOG ,findTimeLog.data() # endif ); #endif return(transactionFound); }
void SipTransactionList::removeOldTransactions(long oldTransaction, long oldInviteTransaction) { SipTransaction** transactionsToBeDeleted = NULL; int deleteCount = 0; int busyCount = 0; # ifdef TIME_LOG OsTimeLog gcTimes; gcTimes.addEvent("start"); # endif lock(); # ifdef TIME_LOG gcTimes.addEvent("locked"); # endif int numTransactions = mTransactions.entries(); if(numTransactions > 0) { UtlHashBagIterator iterator(mTransactions); SipTransaction* transactionFound = NULL; long transTime; // Pull all of the transactions to be deleted out of the list while ((transactionFound = (SipTransaction*) iterator())) { if(transactionFound->isBusy()) { busyCount++; } else { transTime = transactionFound->getTimeStamp(); // Invites need to be kept longer than other transactions if ( transTime < ( transactionFound->isMethod(SIP_INVITE_METHOD) ? oldInviteTransaction : oldTransaction ) ) { #ifdef TRANSACTION_MATCH_DEBUG Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipTransactionList::removeOldTransactions " " removing %p", transactionFound ); #endif // Remove it from the list mTransactions.removeReference(transactionFound); // Make sure we have a pointer array to hold it if(transactionsToBeDeleted == NULL) { transactionsToBeDeleted = new SipTransaction*[numTransactions]; } // Put it in the pointer array transactionsToBeDeleted[deleteCount] = transactionFound; deleteCount++; // Make sure the events waiting for the transaction // to be available are signaled before we delete // any of the transactions or we end up with // incomplete transaction trees (i.e. deleted branches) // :TODO: move to the actual deletion loop so we're not holding the lock? -SDL transactionFound->signalAllAvailable(); } } } } # ifdef TIME_LOG gcTimes.addEvent("scan done"); # endif // We do not need the lock now that the transactions have been // removed from the list unlock(); if ( deleteCount || busyCount ) // do not log 'doing nothing when nothing to do', even at debug { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipTransactionList::removeOldTransactions" " deleting %d of %d transactions (%d busy)", deleteCount , numTransactions, busyCount ); } // Delete the transactions in the array if (transactionsToBeDeleted) { # ifdef TIME_LOG gcTimes.addEvent("start delete"); # endif for(int txIndex = 0; txIndex < deleteCount; txIndex++) { delete transactionsToBeDeleted[txIndex]; } # ifdef TIME_LOG gcTimes.addEvent("finish delete"); # endif delete[] transactionsToBeDeleted; } # ifdef TIME_LOG UtlString timeString; gcTimes.getLogString(timeString); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipTransactionList::removeOldTransactions " "%s", timeString.data() ); # endif }