diff options
author | Rider Linden <rider@lindenlab.com> | 2016-01-25 17:10:29 -0800 |
---|---|---|
committer | Rider Linden <rider@lindenlab.com> | 2016-01-25 17:10:29 -0800 |
commit | cf77fea0cd7aff5eef6de69115ec6a296a025934 (patch) | |
tree | 82a60a0bf1a1bc630c434e08b4210095ea67becd | |
parent | de81e34dc6049d502031d7de380cdd7d6fa2f458 (diff) |
MAINT-6086: Reworked how sessions were being tracked and recovered. A case was occurring where a session was being created and then destroyed, but had never been added to the session tracking map.
-rwxr-xr-x | indra/newview/llvoicevivox.cpp | 314 | ||||
-rwxr-xr-x | indra/newview/llvoicevivox.h | 59 |
2 files changed, 284 insertions, 89 deletions
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index e9e004f492..0239a8ad21 100755 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -25,6 +25,7 @@ */ #include "llviewerprecompiledheaders.h" +#include <algorithm> #include "llvoicevivox.h" #include "llsdutil.h" @@ -3134,7 +3135,7 @@ void LLVivoxVoiceClient::sessionRemovedEvent( leftAudioSession(session); // This message invalidates the session's handle. Set it to empty. - setSessionHandle(session); + clearSessionHandle(session); // This also means that the session's session group is now empty. // Terminate the session group so it doesn't leak. @@ -4164,6 +4165,8 @@ void LLVivoxVoiceClient::callUser(const LLUUID &uuid) switchChannel(userURI, false, true, true); } +#if 0 +// Vivox text IMs are not in use. LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::startUserIMSession(const LLUUID &uuid) { // Figure out if a session with the user already exists @@ -4197,11 +4200,14 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::startUserIMSession(con return session; } - +#endif void LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid) { - // Figure out if a session with the user exists +#if 0 + // Vivox text IMs are not in use. + + // Figure out if a session with the user exists sessionStatePtr_t session(findSession(uuid)); if(session) { @@ -4215,6 +4221,7 @@ void LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid) { LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL; } +#endif } bool LLVivoxVoiceClient::isValidChannel(std::string &sessionHandle) { @@ -4256,7 +4263,7 @@ BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id) BOOL result = TRUE; sessionStatePtr_t session(findSession(id)); - if(session != NULL) + if(session) { // this is a p2p session with the indicated caller, or the session with the specified UUID. if(session->mSynthesizedCallerID) @@ -4265,10 +4272,10 @@ BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id) else { // Didn't find a matching session -- check the current audio session for a matching participant - if(mAudioSession != NULL) + if(mAudioSession) { participantStatePtr_t participant(findParticipantByID(id)); - if(participant != NULL) + if(participant) { result = participant->isAvatar(); } @@ -5027,29 +5034,49 @@ void LLVivoxVoiceClient::filePlaybackSetMode(bool vox, float speed) // TODO: Implement once Vivox gives me a sample } +//------------------------------------------------------------------------ +std::set<LLVivoxVoiceClient::sessionState::wptr_t> LLVivoxVoiceClient::sessionState::mSession; + + LLVivoxVoiceClient::sessionState::sessionState() : - mErrorStatusCode(0), - mMediaStreamState(streamStateUnknown), - //mTextStreamState(streamStateUnknown), - mCreateInProgress(false), - mMediaConnectInProgress(false), - mVoiceInvitePending(false), - mTextInvitePending(false), - mSynthesizedCallerID(false), - mIsChannel(false), - mIsSpatial(false), - mIsP2P(false), - mIncoming(false), - mVoiceEnabled(false), - mReconnect(false), - mVolumeDirty(false), - mMuteDirty(false), - mParticipantsChanged(false) + mErrorStatusCode(0), + mMediaStreamState(streamStateUnknown), + mCreateInProgress(false), + mMediaConnectInProgress(false), + mVoiceInvitePending(false), + mTextInvitePending(false), + mSynthesizedCallerID(false), + mIsChannel(false), + mIsSpatial(false), + mIsP2P(false), + mIncoming(false), + mVoiceEnabled(false), + mReconnect(false), + mVolumeDirty(false), + mMuteDirty(false), + mParticipantsChanged(false) +{ +} + +/*static*/ +LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::createSession() { + sessionState::ptr_t ptr(new sessionState()); + + std::pair<std::set<wptr_t>::iterator, bool> result = mSession.insert(ptr); + + if (result.second) + ptr->mMyIterator = result.first; + + return ptr; } LLVivoxVoiceClient::sessionState::~sessionState() { + LL_INFOS("Voice") << "Destroying session handle=" << mHandle << " SIP=" << mSIPURI << LL_ENDL; + if (mMyIterator != mSession.end()) + mSession.erase(mMyIterator); + removeAllParticipants(); } @@ -5068,16 +5095,112 @@ bool LLVivoxVoiceClient::sessionState::isTextIMPossible() } -LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsBegin(void) +/*static*/ +LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchSessionByHandle(const std::string &handle) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set<wptr_t>::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByHandle, _1, handle)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +/*static*/ +LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchCreatingSessionByURI(const std::string &uri) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set<wptr_t>::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCreatingURI, _1, uri)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +/*static*/ +LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchSessionByURI(const std::string &uri) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set<wptr_t>::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testBySIPOrAlterateURI, _1, uri)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +/*static*/ +LLVivoxVoiceClient::sessionState::ptr_t LLVivoxVoiceClient::sessionState::matchSessionByParticipant(const LLUUID &participant_id) +{ + sessionStatePtr_t result; + + // *TODO: My kingdom for a lambda! + std::set<wptr_t>::iterator it = std::find_if(mSession.begin(), mSession.end(), boost::bind(testByCallerId, _1, participant_id)); + + if (it != mSession.end()) + result = (*it).lock(); + + return result; +} + +void LLVivoxVoiceClient::sessionState::for_each(sessionFunc_t func) +{ + std::for_each(mSession.begin(), mSession.end(), boost::bind(for_eachPredicate, _1, func)); +} + +// simple test predicates. +// *TODO: These should be made into lambdas when we can pull the trigger on newer C++ features. +bool LLVivoxVoiceClient::sessionState::testByHandle(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string handle) { - return mSessions.begin(); + ptr_t aLock(a.lock()); + + return aLock ? aLock->mHandle == handle : false; +} + +bool LLVivoxVoiceClient::sessionState::testByCreatingURI(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string uri) +{ + ptr_t aLock(a.lock()); + + return aLock ? (aLock->mCreateInProgress && (aLock->mSIPURI == uri)) : false; +} + +bool LLVivoxVoiceClient::sessionState::testBySIPOrAlterateURI(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string uri) +{ + ptr_t aLock(a.lock()); + + return aLock ? ((aLock->mSIPURI == uri) || (aLock->mAlternateSIPURI == uri)) : false; } -LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsEnd(void) + +bool LLVivoxVoiceClient::sessionState::testByCallerId(const LLVivoxVoiceClient::sessionState::wptr_t &a, LLUUID participantId) { - return mSessions.end(); + ptr_t aLock(a.lock()); + + return aLock ? ((aLock->mCallerID == participantId) || (aLock->mIMSessionID == participantId)) : false; } +/*static*/ +void LLVivoxVoiceClient::sessionState::for_eachPredicate(const LLVivoxVoiceClient::sessionState::wptr_t &a, sessionFunc_t func) +{ + ptr_t aLock(a.lock()); + + if (aLock) + func(aLock); + else + { + LL_WARNS("Voice") << "Stale handle in session map!" << LL_ENDL; + } +} + + LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSession(const std::string &handle) { @@ -5093,33 +5216,14 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSession(const std: LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) { - sessionStatePtr_t result; - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionStatePtr_t session = *iter; - if(session->mCreateInProgress && (session->mSIPURI == uri)) - { - result = session; - break; - } - } + sessionStatePtr_t result = sessionState::matchCreatingSessionByURI(uri); return result; } LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::findSession(const LLUUID &participant_id) { - sessionStatePtr_t result; - - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionStatePtr_t session = *iter; - if((session->mCallerID == participant_id) || (session->mIMSessionID == participant_id)) - { - result = session; - break; - } - } + sessionStatePtr_t result = sessionState::matchSessionByParticipant(participant_id); return result; } @@ -5130,18 +5234,9 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std:: if(handle.empty()) { - // No handle supplied. - // Check whether there's already a session with this URI - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionStatePtr_t s(*iter); - if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri)) - { - // TODO: I need to think about this logic... it's possible that this case should raise an internal error. - result = s; - break; - } - } + // No handle supplied. + // Check whether there's already a session with this URI + result = sessionState::matchSessionByURI(uri); } else // (!handle.empty()) { @@ -5158,8 +5253,8 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std:: { // No existing session found. - LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL; - result.reset(new sessionState()); + LL_DEBUGS("Voice") << "adding new session: handle \"" << handle << "\" URI " << uri << LL_ENDL; + result = sessionState::createSession(); result->mSIPURI = uri; result->mHandle = handle; @@ -5168,10 +5263,11 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std:: result->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); } - mSessions.insert(result); - if(!result->mHandle.empty()) { + // *TODO: Rider: This concerns me. There is a path (via switchChannel) where + // we do not track the session. In theory this means that we could end up with + // a mAuidoSession that does not match the session tracked in mSessionsByHandle mSessionsByHandle.insert(sessionMap::value_type(result->mHandle, result)); } } @@ -5209,6 +5305,30 @@ LLVivoxVoiceClient::sessionStatePtr_t LLVivoxVoiceClient::addSession(const std:: return result; } +void LLVivoxVoiceClient::clearSessionHandle(const sessionStatePtr_t &session) +{ + if (session) + { + if (session->mHandle.empty()) + { + sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); + if (iter != mSessionsByHandle.end()) + { + mSessionsByHandle.erase(iter); + } + } + else + { + LL_WARNS("Voice") << "Session has empty handle!" << LL_ENDL; + } + } + else + { + LL_WARNS("Voice") << "Attempt to clear NULL session!" << LL_ENDL; + } + +} + void LLVivoxVoiceClient::setSessionHandle(const sessionStatePtr_t &session, const std::string &handle) { // Have to remove the session from the handle-indexed map before changing the handle, or things will break badly. @@ -5221,14 +5341,14 @@ void LLVivoxVoiceClient::setSessionHandle(const sessionStatePtr_t &session, cons { if(iter->second != session) { - LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL; + LL_ERRS("Voice") << "Internal error: session mismatch! Session may have been duplicated." << LL_ENDL; } mSessionsByHandle.erase(iter); } else { - LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL; + LL_WARNS("Voice") << "Attempt to remove session with handle " << session->mHandle << " not found in map!" << LL_ENDL; } } @@ -5266,9 +5386,6 @@ void LLVivoxVoiceClient::deleteSession(const sessionStatePtr_t &session) } } - // Remove the session from the URI map - mSessions.erase(session); - // At this point, the session should be unhooked from all lists and all state should be consistent. verifySessionState(); @@ -5285,27 +5402,23 @@ void LLVivoxVoiceClient::deleteSession(const sessionStatePtr_t &session) mNextAudioSession.reset(); } - // delete the session - //delete session; } void LLVivoxVoiceClient::deleteAllSessions() { LL_DEBUGS("Voice") << "called" << LL_ENDL; - while(!mSessions.empty()) + + while (!mSessionsByHandle.empty()) { - deleteSession(*(sessionsBegin())); + deleteSession(mSessionsByHandle.begin()->second); } - if(!mSessionsByHandle.empty()) - { - LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL; - } } void LLVivoxVoiceClient::verifySessionState(void) { +#if 0 // This is mostly intended for debugging problems with session state management. LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL; @@ -5332,7 +5445,9 @@ void LLVivoxVoiceClient::verifySessionState(void) } } } +#endif +#if 0 // check that every entry in the handle map points to a valid session in the session set for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++) { @@ -5350,6 +5465,7 @@ void LLVivoxVoiceClient::verifySessionState(void) } } } +#endif } void LLVivoxVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) @@ -5500,8 +5616,51 @@ void LLVivoxVoiceClient::onAvatarNameCache(const LLUUID& agent_id, avatarNameResolved(agent_id, display_name); } +void LLVivoxVoiceClient::predAvatarNameResolution(const LLVivoxVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name) +{ + participantStatePtr_t participant(session->findParticipantByID(id)); + if (participant) + { + // Found -- fill in the name + participant->mAccountName = name; + // and post a "participants updated" message to listeners later. + session->mParticipantsChanged = true; + } + + // Check whether this is a p2p session whose caller name just resolved + if (session->mCallerID == id) + { + // this session's "caller ID" just resolved. Fill in the name. + session->mName = name; + if (session->mTextInvitePending) + { + session->mTextInvitePending = false; + + // We don't need to call LLIMMgr::getInstance()->addP2PSession() here. The first incoming message will create the panel. + } + if (session->mVoiceInvitePending) + { + session->mVoiceInvitePending = false; + + LLIMMgr::getInstance()->inviteToSession( + session->mIMSessionID, + session->mName, + session->mCallerID, + session->mName, + IM_SESSION_P2P_INVITE, + LLIMMgr::INVITATION_TYPE_VOICE, + session->mHandle, + session->mSIPURI); + } + + } +} + void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) { +#if 1 + sessionState::for_each(boost::bind(predAvatarNameResolution, _1, id, name)); +#else // Iterate over all sessions. for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) { @@ -5544,6 +5703,7 @@ void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string } } +#endif } bool LLVivoxVoiceClient::setVoiceEffect(const LLUUID& id) diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 0054c7cca4..1e59a337f5 100755 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -116,7 +116,7 @@ public: // close any existing text IM session with the specified user virtual void endUserIMSession(const LLUUID &uuid); - + // Returns true if calling back the session URI after the session has closed is possible. // Currently this will be false only for PSTN P2P calls. // NOTE: this will return true if the session can't be found. @@ -256,6 +256,7 @@ protected: friend class LLVivoxVoiceClientMuteListObserver; friend class LLVivoxVoiceClientFriendsObserver; + enum streamState { @@ -302,22 +303,32 @@ protected: struct sessionState { - public: - sessionState(); + public: + typedef boost::shared_ptr<sessionState> ptr_t; + typedef boost::weak_ptr<sessionState> wptr_t; + + typedef boost::function<void(const ptr_t &)> sessionFunc_t; + + static ptr_t createSession(); ~sessionState(); participantStatePtr_t addParticipant(const std::string &uri); - // Note: after removeParticipant returns, the participant* that was passed to it will have been deleted. - // Take care not to use the pointer again after that. void removeParticipant(const participantStatePtr_t &participant); void removeAllParticipants(); - + participantStatePtr_t findParticipant(const std::string &uri); participantStatePtr_t findParticipantByID(const LLUUID& id); - + + static ptr_t matchSessionByHandle(const std::string &handle); + static ptr_t matchCreatingSessionByURI(const std::string &uri); + static ptr_t matchSessionByURI(const std::string &uri); + static ptr_t matchSessionByParticipant(const LLUUID &participant_id); + bool isCallBackPossible(); bool isTextIMPossible(); + static void for_each(sessionFunc_t func); + std::string mHandle; std::string mGroupHandle; std::string mSIPURI; @@ -354,6 +365,20 @@ protected: participantUUIDMap mParticipantsByUUID; LLUUID mVoiceFontID; + + private: + sessionState(); + + static std::set<wptr_t> mSession; // canonical list of outstanding sessions. + std::set<wptr_t>::iterator mMyIterator; // used for delete + + static void for_eachPredicate(const wptr_t &a, sessionFunc_t func); + + static bool testByHandle(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string handle); + static bool testByCreatingURI(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string uri); + static bool testBySIPOrAlterateURI(const LLVivoxVoiceClient::sessionState::wptr_t &a, std::string uri); + static bool testByCallerId(const LLVivoxVoiceClient::sessionState::wptr_t &a, LLUUID participantId); + }; typedef boost::shared_ptr<sessionState> sessionStatePtr_t; @@ -362,7 +387,9 @@ protected: /////////////////////////////////////////////////////// // Private Member Functions ////////////////////////////////////////////////////// - + + + ////////////////////////////// /// @name TVC/Server management and communication //@{ @@ -479,6 +506,7 @@ protected: participantStatePtr_t findParticipantByID(const LLUUID& id); +#if 0 //////////////////////////////////////// // voice sessions. typedef std::set<sessionStatePtr_t> sessionSet; @@ -486,14 +514,15 @@ protected: typedef sessionSet::iterator sessionIterator; sessionIterator sessionsBegin(void); sessionIterator sessionsEnd(void); +#endif sessionStatePtr_t findSession(const std::string &handle); sessionStatePtr_t findSessionBeingCreatedByURI(const std::string &uri); sessionStatePtr_t findSession(const LLUUID &participant_id); - sessionStatePtr_t findSessionByCreateID(const std::string &create_id); - sessionStatePtr_t addSession(const std::string &uri, const std::string &handle = LLStringUtil::null); - void setSessionHandle(const sessionStatePtr_t &session, const std::string &handle = LLStringUtil::null); + sessionStatePtr_t addSession(const std::string &uri, const std::string &handle = std::string()); + void clearSessionHandle(const sessionStatePtr_t &session); + void setSessionHandle(const sessionStatePtr_t &session, const std::string &handle); void setSessionURI(const sessionStatePtr_t &session, const std::string &uri); void deleteSession(const sessionStatePtr_t &session); void deleteAllSessions(void); @@ -564,6 +593,8 @@ protected: void lookupName(const LLUUID &id); void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); void avatarNameResolved(const LLUUID &id, const std::string &name); + static void predAvatarNameResolution(const LLVivoxVoiceClient::sessionStatePtr_t &session, LLUUID id, std::string name); + boost::signals2::connection mAvatarNameCacheConnection; ///////////////////////////// @@ -675,7 +706,9 @@ private: int mLoginRetryCount; sessionMap mSessionsByHandle; // Active sessions, indexed by session handle. Sessions which are being initiated may not be in this map. +#if 0 sessionSet mSessions; // All sessions, not indexed. This is the canonical session list. +#endif bool mBuddyListMapPopulated; bool mBlockRulesListReceived; @@ -720,10 +753,12 @@ private: void sendFriendsListUpdates(); +#if 0 // start a text IM session with the specified user // This will be asynchronous, the session may be established at a future time. sessionStatePtr_t startUserIMSession(const LLUUID& uuid); - +#endif + void enforceTether(void); bool mSpatialCoordsDirty; |