summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpepper <3782201+rohvani@users.noreply.github.com>2026-09-21 17:15:04 -0400
committerpepper <3782201+rohvani@users.noreply.github.com>2026-09-23 15:24:01 -0400
commite48a615aa0ff9848ce45caef8974313d92a05b2e (patch)
tree9771dc1b39644778a3e92ca7790bf6e8793dd463
parent2668047ea5fc8c6a271621510db8eb25a2b24502 (diff)
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.
-rw-r--r--indra/newview/llchatservicehistory.cpp12
-rw-r--r--indra/newview/llfloaterconversationpreview.cpp12
-rw-r--r--indra/newview/llfloaterconversationpreview.h1
-rw-r--r--indra/newview/lllogchat.cpp131
-rw-r--r--indra/newview/lllogchat.h14
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 <set>
#include <sstream>
-// Keep exact-path legacy parsing behind LLLogChat's narrow stitching bridge.
-struct LLChatServiceHistoryAccess
-{
- static void loadLegacy(const std::string& path, std::list<LLSD>& 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<LLSD>* 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<LLSD>* messages, const std
getChild<LLTextBox>("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 <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/local_time_adjustor.hpp>
+#include <mutex>
#if !LL_WINDOWS
#include <fcntl.h>
@@ -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<LLMutex> 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
@@ -590,18 +592,6 @@ void LLLogChat::getTranscriptFamily(const std::string& file_name, std::vector<st
void LLLogChat::loadChatHistoryExact(const std::string& path, std::list<LLSD>& messages,
const LLSD& load_params)
{
- if (LLChatServiceHistory::historySuppressed())
- {
- return;
- }
-
- loadChatHistoryExactUnchecked(path, messages, load_params);
-}
-
-// static
-void LLLogChat::loadChatHistoryExactUnchecked(
- const std::string& path, std::list<LLSD>& messages, const LLSD& load_params)
-{
if (path.empty())
{
return;
@@ -638,17 +628,6 @@ bool LLLogChat::historyThreadsFinished(LLUUID session_id)
return finished;
}
-LLLoadHistoryThread* LLLogChat::getLoadHistoryThread(LLUUID session_id)
-{
- LLMutexLock lock(historyThreadsMutex());
- std::map<LLUUID,LLLoadHistoryThread *>::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,17 +882,11 @@ 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)
{
// Snapshot and remove the exact legacy targets while plaintext writers are excluded.
@@ -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::list<LL
if (!fptr)
{
mNewLoad = false;
- (*mLoadEndSignal)(messages, file_name);
+ mLoadEndSignal(messages, file_name);
return; //No previous conversation with this name.
}
}
@@ -1357,7 +1329,7 @@ void LLLoadHistoryThread::loadHistory(const std::string& file_name, std::list<LL
{
fclose(fptr);
mNewLoad = false;
- (*mLoadEndSignal)(messages, file_name);
+ mLoadEndSignal(messages, file_name);
return;
}
}
@@ -1401,25 +1373,10 @@ void LLLoadHistoryThread::loadHistory(const std::string& file_name, std::list<LL
fclose(fptr);
mNewLoad = false;
- (*mLoadEndSignal)(messages, file_name);
+ mLoadEndSignal(messages, file_name);
}
boost::signals2::connection LLLoadHistoryThread::setLoadEndSignal(const load_end_signal_t::slot_type& cb)
{
- if (NULL == mLoadEndSignal)
- {
- mLoadEndSignal = new load_end_signal_t();
- }
-
- return mLoadEndSignal->connect(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<LLSD>* mMessages;
LLSD mLoadParams;
bool mNewLoad;
@@ -61,9 +62,8 @@ public:
virtual void run();
typedef boost::signals2::signal<void (std::list<LLSD>* 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<std::string>& 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<LLSD>& 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<LLSD>& messages,
- const LLSD& load_params = LLSD());
static std::string cleanFileName(std::string filename);
LLMutex* historyThreadsMutex();