From e48a615aa0ff9848ce45caef8974313d92a05b2e Mon Sep 17 00:00:00 2001 From: pepper <3782201+rohvani@users.noreply.github.com> Date: Mon, 21 Sep 2026 17:15:04 -0400 Subject: Simplify transcript worker ownership and publication boundaries Let the legacy worker own its filename and completion signal. Remove manual signal deletion and the unused thread accessor together with Preview's mutex; both loaders publish pages on the main loop. Keep save/delete exclusion and release it before notifying observers. Expose the captured-path reader directly, with account and privacy checks at main-loop dispatch and publication, and remove the redundant bridge. --- indra/newview/llchatservicehistory.cpp | 12 +-- indra/newview/llfloaterconversationpreview.cpp | 12 +-- indra/newview/llfloaterconversationpreview.h | 1 - indra/newview/lllogchat.cpp | 131 +++++++++---------------- indra/newview/lllogchat.h | 14 +-- 5 files changed, 51 insertions(+), 119 deletions(-) diff --git a/indra/newview/llchatservicehistory.cpp b/indra/newview/llchatservicehistory.cpp index 954d534641..c21b5e2935 100644 --- a/indra/newview/llchatservicehistory.cpp +++ b/indra/newview/llchatservicehistory.cpp @@ -56,16 +56,6 @@ #include #include -// Keep exact-path legacy parsing behind LLLogChat's narrow stitching bridge. -struct LLChatServiceHistoryAccess -{ - static void loadLegacy(const std::string& path, std::list& messages, - const LLSD& parameters) - { - LLLogChat::loadChatHistoryExactUnchecked(path, messages, parameters); - } -}; - namespace { using namespace LLChatServiceHistoryCore; @@ -2803,7 +2793,7 @@ LLChatServiceHistory::HistoryResult readStitched( parameters["cut_off_todays_date"] = false; for (const std::string& path : legacy_paths) { - LLChatServiceHistoryAccess::loadLegacy(path, legacy, parameters); + LLLogChat::loadChatHistoryExact(path, legacy, parameters); } const F64 service_epoch = archive.has_oldest diff --git a/indra/newview/llfloaterconversationpreview.cpp b/indra/newview/llfloaterconversationpreview.cpp index 3221abaeca..4f39b65371 100644 --- a/indra/newview/llfloaterconversationpreview.cpp +++ b/indra/newview/llfloaterconversationpreview.cpp @@ -52,7 +52,6 @@ LLFloaterConversationPreview::LLFloaterConversationPreview(const LLSD& session_i mPageSize(CONVERSATION_HISTORY_PAGE_SIZE), mAccountName(session_id[LL_FCP_ACCOUNT_NAME]), mCompleteName(session_id[LL_FCP_COMPLETE_NAME]), - mMutex(), mShowHistory(false), mMessages(NULL), mHistoryThreadsBusy(false), @@ -127,9 +126,6 @@ void LLFloaterConversationPreview::setPages(std::list* messages, const std { if(file_name == mChatHistoryFileName && messages) { - // additional protection to avoid changes of mMessages in setPages() - LLMutexLock lock(&mMutex); - // Preserve the reader's distance from the newest page while asynchronous // reloads replace the backing list. const S32 old_last_page = mMessages && !mMessages->empty() @@ -155,11 +151,6 @@ void LLFloaterConversationPreview::setPages(std::list* messages, const std getChild("page_num_label")->setValue(total_page_num); mShowHistory = true; } - LLLoadHistoryThread* loadThread = LLLogChat::getInstance()->getLoadHistoryThread(mSessionID); - if (loadThread) - { - loadThread->removeLoadEndSignal(boost::bind(&LLFloaterConversationPreview::setPages, this, _1, _2)); - } } void LLFloaterConversationPreview::draw() @@ -364,8 +355,7 @@ void LLFloaterConversationPreview::invalidateHistory() void LLFloaterConversationPreview::showHistory() { - // additional protection to avoid changes of mMessages in setPages - LLMutexLock lock(&mMutex); + // Both loaders publish on the main loop, so pages stay stable during rendering. mChatHistory->clear(); if(mMessages == NULL || !mMessages->size() || mCurrentPage * mPageSize >= mMessages->size()) { diff --git a/indra/newview/llfloaterconversationpreview.h b/indra/newview/llfloaterconversationpreview.h index 521acb7d4a..d1228d3332 100644 --- a/indra/newview/llfloaterconversationpreview.h +++ b/indra/newview/llfloaterconversationpreview.h @@ -60,7 +60,6 @@ private: void showHistory(); void startLegacyLoad(); - LLMutex mMutex; LLSpinCtrl* mPageSpinner; LLChatHistory* mChatHistory; LLUUID mSessionID; diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 306d490fff..060352ab4c 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #if !LL_WINDOWS #include @@ -432,55 +433,56 @@ void LLLogChat::saveHistory(const std::string& filename, return; } + // Keep open/write/close on one side of the deletion sweep, then unlock before + // notifying observers that may read or write transcripts. + std::unique_lock mutation_lock(sTranscriptMutationMutex); + llofstream file(LLLogChat::makeLogFileName(filename).c_str(), std::ios_base::app); + if (!file.is_open()) { - // Keep the whole open/write/flush/close operation on one side of a Delete sweep. - LLMutexLock mutation_lock(&sTranscriptMutationMutex); - llofstream file(LLLogChat::makeLogFileName(filename).c_str(), std::ios_base::app); - if (!file.is_open()) - { - LL_WARNS() << "Couldn't open chat history log! - " + filename << LL_ENDL; - return; - } + LL_WARNS() << "Couldn't open chat history log! - " + filename << LL_ENDL; + return; + } - std::string altered_line = line; + std::string altered_line = line; // avoid costly regex calls - if (line.find("/mention") != std::string::npos) - { - static const boost::regex mention_regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/mention", boost::regex::perl | boost::regex::icase); + if (line.find("/mention") != std::string::npos) + { + static const boost::regex mention_regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/mention", boost::regex::perl | boost::regex::icase); // replace mention URL with [@username](URL) - altered_line = boost::regex_replace(line, mention_regex, [](const boost::smatch& match) -> std::string - { - std::string url = match[0].str(); - std::string username = LLUrlAction::getURLLabel(url); - return "[" + username + "](" + url + ")"; - }); - } + altered_line = boost::regex_replace(line, mention_regex, [](const boost::smatch& match) -> std::string + { + std::string url = match[0].str(); + std::string username = LLUrlAction::getURLLabel(url); + return "[" + username + "](" + url + ")"; + }); + } - LLSD item; + LLSD item; - if (gSavedPerAccountSettings.getBOOL("LogTimestamp")) - item["time"] = LLLogChat::timestamp2LogString(0, gSavedPerAccountSettings.getBOOL("LogTimestampDate")); + if (gSavedPerAccountSettings.getBOOL("LogTimestamp")) + item["time"] = LLLogChat::timestamp2LogString(0, gSavedPerAccountSettings.getBOOL("LogTimestampDate")); - item["from_id"] = from_id; - item["message"] = altered_line; + item["from_id"] = from_id; + item["message"] = altered_line; //adding "Second Life:" for all system messages to make chat log history parsing more reliable - if (from.empty() && from_id.isNull()) - { - item["from"] = SYSTEM_FROM; - } - else - { - item["from"] = from; - } - - file << LLChatLogFormatter(item) << std::endl; - file.close(); + if (from.empty() && from_id.isNull()) + { + item["from"] = SYSTEM_FROM; + } + else + { + item["from"] = from; } - LLLogChat::notifyTranscriptCreated(); + file << LLChatLogFormatter(item) << std::endl; + + file.close(); + mutation_lock.unlock(); + + LLLogChat::getInstance()->triggerHistorySignal(); } // static @@ -589,18 +591,6 @@ void LLLogChat::getTranscriptFamily(const std::string& file_name, std::vector& messages, const LLSD& load_params) -{ - if (LLChatServiceHistory::historySuppressed()) - { - return; - } - - loadChatHistoryExactUnchecked(path, messages, load_params); -} - -// static -void LLLogChat::loadChatHistoryExactUnchecked( - const std::string& path, std::list& messages, const LLSD& load_params) { if (path.empty()) { @@ -638,17 +628,6 @@ bool LLLogChat::historyThreadsFinished(LLUUID session_id) return finished; } -LLLoadHistoryThread* LLLogChat::getLoadHistoryThread(LLUUID session_id) -{ - LLMutexLock lock(historyThreadsMutex()); - std::map::iterator it = mLoadHistoryThreads.find(session_id); - if (it != mLoadHistoryThreads.end()) - { - return it->second; - } - return NULL; -} - LLDeleteHistoryThread* LLLogChat::getDeleteHistoryThread(LLUUID session_id) { LLMutexLock lock(historyThreadsMutex()); @@ -903,16 +882,10 @@ bool LLLogChat::moveTranscripts(const std::string currentDirectory, //static void LLLogChat::deleteTranscripts() { - deleteTranscriptContent(); + deleteTranscriptContent(gDirUtilp->getPerAccountChatLogsDir()); LLFloaterIMSessionTab::processChatHistoryStyleUpdate(true); } -// static -bool LLLogChat::deleteTranscriptContent() -{ - return deleteTranscriptContent(gDirUtilp->getPerAccountChatLogsDir()); -} - // static bool LLLogChat::deleteTranscriptContent(const std::string& directory) { @@ -1286,8 +1259,7 @@ LLLoadHistoryThread::LLLoadHistoryThread(const std::string& file_name, std::list mMessages(messages), mFileName(file_name), mLoadParams(load_params), - mNewLoad(true), - mLoadEndSignal(NULL) + mNewLoad(true) { } @@ -1338,7 +1310,7 @@ void LLLoadHistoryThread::loadHistory(const std::string& file_name, std::listconnect(cb); -} - -void LLLoadHistoryThread::removeLoadEndSignal(const load_end_signal_t::slot_type& cb) -{ - if (NULL != mLoadEndSignal) - { - mLoadEndSignal->disconnect_all_slots(); - delete mLoadEndSignal; - } - mLoadEndSignal = NULL; + return mLoadEndSignal.connect(cb); } diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index 468093eff7..5e24a658b3 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -49,7 +49,8 @@ private: class LLLoadHistoryThread : public LLActionThread { private: - const std::string& mFileName; + // The worker owns its input after the Preview closes. + const std::string mFileName; std::list* mMessages; LLSD mLoadParams; bool mNewLoad; @@ -61,9 +62,8 @@ public: virtual void run(); typedef boost::signals2::signal* messages,const std::string& file_name)> load_end_signal_t; - load_end_signal_t * mLoadEndSignal; + load_end_signal_t mLoadEndSignal; boost::signals2::connection setLoadEndSignal(const load_end_signal_t::slot_type& cb); - void removeLoadEndSignal(const load_end_signal_t::slot_type& cb); }; class LLDeleteHistoryThread : public LLActionThread @@ -112,6 +112,8 @@ public: // Enumerate the ordinary transcript followed by lexical monthly shards, then // reuse the canonical parser for each exact path. static void getTranscriptFamily(const std::string& file_name, std::vector& paths); + // Worker reads use captured paths; the caller checks account/privacy state + // on the main loop before dispatch and again before publishing the result. static void loadChatHistoryExact(const std::string& path, std::list& messages, const LLSD& load_params = LLSD()); @@ -130,7 +132,6 @@ public: // The success-returning sweep lets durable ChatService deletion remain pending // until every legacy transcript target has been removed safely. - static bool deleteTranscriptContent(); static bool deleteTranscriptContent(const std::string& directory); static void notifyTranscriptCreated(); static bool isTranscriptExist(const LLUUID& avatar_id, bool is_group=false); @@ -141,17 +142,12 @@ public: static std::string getGroupChatSuffix(); bool historyThreadsFinished(LLUUID session_id); - LLLoadHistoryThread* getLoadHistoryThread(LLUUID session_id); LLDeleteHistoryThread* getDeleteHistoryThread(LLUUID session_id); bool addLoadHistoryThread(LLUUID& session_id, LLLoadHistoryThread* lthread); bool addDeleteHistoryThread(LLUUID& session_id, LLDeleteHistoryThread* dthread); void cleanupHistoryThreads(); private: - friend struct LLChatServiceHistoryAccess; - static void loadChatHistoryExactUnchecked(const std::string& path, - std::list& messages, - const LLSD& load_params = LLSD()); static std::string cleanFileName(std::string filename); LLMutex* historyThreadsMutex(); -- cgit v1.3