MAINT-3476 FIX Opening large chat histories from conversation log eats up huge amounts of memory, leading to viewer crash.
parent
21d9e524f6
commit
443c502ccc
|
|
@ -32,6 +32,7 @@
|
|||
#include "llfloaterimnearbychat.h"
|
||||
#include "llspinctrl.h"
|
||||
#include "lltrans.h"
|
||||
#include "llnotificationsutil.h"
|
||||
|
||||
const std::string LL_FCP_COMPLETE_NAME("complete_name");
|
||||
const std::string LL_FCP_ACCOUNT_NAME("user_name");
|
||||
|
|
@ -45,14 +46,20 @@ LLFloaterConversationPreview::LLFloaterConversationPreview(const LLSD& session_i
|
|||
mAccountName(session_id[LL_FCP_ACCOUNT_NAME]),
|
||||
mCompleteName(session_id[LL_FCP_COMPLETE_NAME]),
|
||||
mMutex(NULL),
|
||||
mShowHistory(false)
|
||||
mShowHistory(false),
|
||||
mMessages(NULL),
|
||||
mHistoryThreadsBusy(false),
|
||||
mOpened(false)
|
||||
{
|
||||
}
|
||||
|
||||
LLFloaterConversationPreview::~LLFloaterConversationPreview()
|
||||
{
|
||||
}
|
||||
|
||||
BOOL LLFloaterConversationPreview::postBuild()
|
||||
{
|
||||
mChatHistory = getChild<LLChatHistory>("chat_history");
|
||||
LLLoadHistoryThread::setLoadEndSignal(boost::bind(&LLFloaterConversationPreview::setPages, this, _1, _2));
|
||||
|
||||
const LLConversation* conv = LLConversationLog::instance().getConversation(mSessionID);
|
||||
std::string name;
|
||||
|
|
@ -79,31 +86,21 @@ BOOL LLFloaterConversationPreview::postBuild()
|
|||
std::string title = getString("Title", args);
|
||||
setTitle(title);
|
||||
|
||||
LLSD load_params;
|
||||
load_params["load_all_history"] = true;
|
||||
load_params["cut_off_todays_date"] = false;
|
||||
|
||||
|
||||
LLSD loading;
|
||||
loading[LL_IM_TEXT] = LLTrans::getString("loading_chat_logs");
|
||||
mMessages.push_back(loading);
|
||||
mPageSpinner = getChild<LLSpinCtrl>("history_page_spin");
|
||||
mPageSpinner->setCommitCallback(boost::bind(&LLFloaterConversationPreview::onMoreHistoryBtnClick, this));
|
||||
mPageSpinner->setMinValue(1);
|
||||
mPageSpinner->set(1);
|
||||
mPageSpinner->setEnabled(false);
|
||||
LLLogChat::startChatHistoryThread(file, load_params);
|
||||
return LLFloater::postBuild();
|
||||
}
|
||||
|
||||
void LLFloaterConversationPreview::setPages(std::list<LLSD>& messages, const std::string& file_name)
|
||||
void LLFloaterConversationPreview::setPages(std::list<LLSD>* messages, const std::string& file_name)
|
||||
{
|
||||
if(file_name == mChatHistoryFileName)
|
||||
if(file_name == mChatHistoryFileName && messages)
|
||||
{
|
||||
// additional protection to avoid changes of mMessages in setPages()
|
||||
LLMutexLock lock(&mMutex);
|
||||
if (mMessages)
|
||||
{
|
||||
delete mMessages; // Clean up temporary message list with "Loading..." text
|
||||
}
|
||||
mMessages = messages;
|
||||
mCurrentPage = (mMessages.size() ? (mMessages.size() - 1) / mPageSize : 0);
|
||||
mCurrentPage = (mMessages->size() ? (mMessages->size() - 1) / mPageSize : 0);
|
||||
|
||||
mPageSpinner->setEnabled(true);
|
||||
mPageSpinner->setMaxValue(mCurrentPage+1);
|
||||
|
|
@ -113,6 +110,11 @@ void LLFloaterConversationPreview::setPages(std::list<LLSD>& messages, const std
|
|||
getChild<LLTextBox>("page_num_label")->setValue(total_page_num);
|
||||
mShowHistory = true;
|
||||
}
|
||||
LLLoadHistoryThread* loadThread = LLLogChat::getLoadHistoryThread(mSessionID);
|
||||
if (loadThread)
|
||||
{
|
||||
loadThread->removeLoadEndSignal(boost::bind(&LLFloaterConversationPreview::setPages, this, _1, _2));
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterConversationPreview::draw()
|
||||
|
|
@ -127,24 +129,82 @@ void LLFloaterConversationPreview::draw()
|
|||
|
||||
void LLFloaterConversationPreview::onOpen(const LLSD& key)
|
||||
{
|
||||
if (mOpened)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mOpened = true;
|
||||
if (!LLLogChat::historyThreadsFinished(mSessionID))
|
||||
{
|
||||
LLNotificationsUtil::add("ChatHistoryIsBusyAlert");
|
||||
mHistoryThreadsBusy = true;
|
||||
closeFloater();
|
||||
return;
|
||||
}
|
||||
LLSD load_params;
|
||||
load_params["load_all_history"] = true;
|
||||
load_params["cut_off_todays_date"] = false;
|
||||
|
||||
// The temporary message list with "Loading..." text
|
||||
// Will be deleted upon loading completion in setPages() method
|
||||
mMessages = new std::list<LLSD>();
|
||||
|
||||
|
||||
LLSD loading;
|
||||
loading[LL_IM_TEXT] = LLTrans::getString("loading_chat_logs");
|
||||
mMessages->push_back(loading);
|
||||
mPageSpinner = getChild<LLSpinCtrl>("history_page_spin");
|
||||
mPageSpinner->setCommitCallback(boost::bind(&LLFloaterConversationPreview::onMoreHistoryBtnClick, this));
|
||||
mPageSpinner->setMinValue(1);
|
||||
mPageSpinner->set(1);
|
||||
mPageSpinner->setEnabled(false);
|
||||
|
||||
// The actual message list to load from file
|
||||
// Will be deleted in a separate thread LLDeleteHistoryThread not to freeze UI
|
||||
// LLDeleteHistoryThread is started in destructor
|
||||
std::list<LLSD>* messages = new std::list<LLSD>();
|
||||
|
||||
LLLogChat::cleanupHistoryThreads();
|
||||
|
||||
LLLoadHistoryThread* loadThread = new LLLoadHistoryThread(mChatHistoryFileName, messages, load_params);
|
||||
loadThread->setLoadEndSignal(boost::bind(&LLFloaterConversationPreview::setPages, this, _1, _2));
|
||||
loadThread->start();
|
||||
LLLogChat::addLoadHistoryThread(mSessionID, loadThread);
|
||||
|
||||
LLDeleteHistoryThread* deleteThread = new LLDeleteHistoryThread(messages, loadThread);
|
||||
LLLogChat::addDeleteHistoryThread(mSessionID, deleteThread);
|
||||
|
||||
mShowHistory = true;
|
||||
}
|
||||
|
||||
void LLFloaterConversationPreview::onClose(bool app_quitting)
|
||||
{
|
||||
mOpened = false;
|
||||
if (!mHistoryThreadsBusy)
|
||||
{
|
||||
LLDeleteHistoryThread* deleteThread = LLLogChat::getDeleteHistoryThread(mSessionID);
|
||||
if (deleteThread)
|
||||
{
|
||||
deleteThread->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterConversationPreview::showHistory()
|
||||
{
|
||||
// additional protection to avoid changes of mMessages in setPages
|
||||
LLMutexLock lock(&mMutex);
|
||||
if(!mMessages.size() || mCurrentPage * mPageSize >= mMessages.size())
|
||||
if(mMessages == NULL || !mMessages->size() || mCurrentPage * mPageSize >= mMessages->size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mChatHistory->clear();
|
||||
std::ostringstream message;
|
||||
std::list<LLSD>::const_iterator iter = mMessages.begin();
|
||||
std::list<LLSD>::const_iterator iter = mMessages->begin();
|
||||
std::advance(iter, mCurrentPage * mPageSize);
|
||||
|
||||
for (int msg_num = 0; iter != mMessages.end() && msg_num < mPageSize; ++iter, ++msg_num)
|
||||
|
||||
for (int msg_num = 0; iter != mMessages->end() && msg_num < mPageSize; ++iter, ++msg_num)
|
||||
{
|
||||
LLSD msg = *iter;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,13 +39,14 @@ class LLFloaterConversationPreview : public LLFloater
|
|||
public:
|
||||
|
||||
LLFloaterConversationPreview(const LLSD& session_id);
|
||||
virtual ~LLFloaterConversationPreview(){};
|
||||
virtual ~LLFloaterConversationPreview();
|
||||
|
||||
virtual BOOL postBuild();
|
||||
void setPages(std::list<LLSD>& messages,const std::string& file_name);
|
||||
void setPages(std::list<LLSD>* messages,const std::string& file_name);
|
||||
|
||||
virtual void draw();
|
||||
virtual void onOpen(const LLSD& key);
|
||||
virtual void onClose(bool app_quitting);
|
||||
|
||||
private:
|
||||
void onMoreHistoryBtnClick();
|
||||
|
|
@ -58,11 +59,13 @@ private:
|
|||
int mCurrentPage;
|
||||
int mPageSize;
|
||||
|
||||
std::list<LLSD> mMessages;
|
||||
std::list<LLSD>* mMessages;
|
||||
std::string mAccountName;
|
||||
std::string mCompleteName;
|
||||
std::string mChatHistoryFileName;
|
||||
std::string mChatHistoryFileName;
|
||||
bool mShowHistory;
|
||||
bool mHistoryThreadsBusy;
|
||||
bool mOpened;
|
||||
};
|
||||
|
||||
#endif /* LLFLOATERCONVERSATIONPREVIEW_H_ */
|
||||
|
|
|
|||
|
|
@ -206,7 +206,11 @@ private:
|
|||
};
|
||||
|
||||
LLLogChat::save_history_signal_t * LLLogChat::sSaveHistorySignal = NULL;
|
||||
LLLoadHistoryThread::load_end_signal_t * LLLoadHistoryThread::mLoadEndSignal = NULL;
|
||||
|
||||
std::map<LLUUID,LLLoadHistoryThread *> LLLogChat::sLoadHistoryThreads;
|
||||
std::map<LLUUID,LLDeleteHistoryThread *> LLLogChat::sDeleteHistoryThreads;
|
||||
LLMutex* LLLogChat::sHistoryThreadsMutex = NULL;
|
||||
|
||||
|
||||
//static
|
||||
std::string LLLogChat::makeLogFileName(std::string filename)
|
||||
|
|
@ -337,83 +341,179 @@ void LLLogChat::saveHistory(const std::string& filename,
|
|||
void LLLogChat::loadChatHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params)
|
||||
{
|
||||
if (file_name.empty())
|
||||
{
|
||||
LL_WARNS("LLLogChat::loadChatHistory") << "Session name is Empty!" << LL_ENDL;
|
||||
return ;
|
||||
}
|
||||
{
|
||||
LL_WARNS("LLLogChat::loadChatHistory") << "Session name is Empty!" << LL_ENDL;
|
||||
return ;
|
||||
}
|
||||
|
||||
bool load_all_history = load_params.has("load_all_history") ? load_params["load_all_history"].asBoolean() : false;
|
||||
bool load_all_history = load_params.has("load_all_history") ? load_params["load_all_history"].asBoolean() : false;
|
||||
|
||||
LLFILE* fptr = LLFile::fopen(LLLogChat::makeLogFileName(file_name), "r");/*Flawfinder: ignore*/
|
||||
if (!fptr)
|
||||
{
|
||||
fptr = LLFile::fopen(LLLogChat::oldLogFileName(file_name), "r");/*Flawfinder: ignore*/
|
||||
if (!fptr)
|
||||
{
|
||||
return; //No previous conversation with this name.
|
||||
}
|
||||
}
|
||||
LLFILE* fptr = LLFile::fopen(LLLogChat::makeLogFileName(file_name), "r");/*Flawfinder: ignore*/
|
||||
if (!fptr)
|
||||
{
|
||||
fptr = LLFile::fopen(LLLogChat::oldLogFileName(file_name), "r");/*Flawfinder: ignore*/
|
||||
if (!fptr)
|
||||
{
|
||||
return; //No previous conversation with this name.
|
||||
}
|
||||
}
|
||||
|
||||
char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/
|
||||
char *bptr;
|
||||
S32 len;
|
||||
bool firstline = TRUE;
|
||||
char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/
|
||||
char *bptr;
|
||||
S32 len;
|
||||
bool firstline = TRUE;
|
||||
|
||||
if (load_all_history || fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END))
|
||||
{ //We need to load the whole historyFile or it's smaller than recall size, so get it all.
|
||||
firstline = FALSE;
|
||||
if (fseek(fptr, 0, SEEK_SET))
|
||||
{
|
||||
fclose(fptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr))
|
||||
{
|
||||
len = strlen(buffer) - 1; /*Flawfinder: ignore*/
|
||||
for (bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0';
|
||||
if (load_all_history || fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END))
|
||||
{ //We need to load the whole historyFile or it's smaller than recall size, so get it all.
|
||||
firstline = FALSE;
|
||||
if (fseek(fptr, 0, SEEK_SET))
|
||||
{
|
||||
fclose(fptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr))
|
||||
{
|
||||
len = strlen(buffer) - 1; /*Flawfinder: ignore*/
|
||||
for (bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0';
|
||||
|
||||
if (firstline)
|
||||
{
|
||||
firstline = FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string line(buffer);
|
||||
|
||||
//updated 1.23 plain text log format requires a space added before subsequent lines in a multilined message
|
||||
if (' ' == line[0])
|
||||
{
|
||||
line.erase(0, MULTI_LINE_PREFIX.length());
|
||||
append_to_last_message(messages, '\n' + line);
|
||||
}
|
||||
else if (0 == len && ('\n' == line[0] || '\r' == line[0]))
|
||||
{
|
||||
//to support old format's multilined messages with new lines used to divide paragraphs
|
||||
append_to_last_message(messages, line);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLSD item;
|
||||
if (!LLChatLogParser::parse(line, item, load_params))
|
||||
{
|
||||
item[LL_IM_TEXT] = line;
|
||||
}
|
||||
messages.push_back(item);
|
||||
}
|
||||
}
|
||||
fclose(fptr);
|
||||
if (firstline)
|
||||
{
|
||||
firstline = FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string line(buffer);
|
||||
|
||||
//updated 1.23 plain text log format requires a space added before subsequent lines in a multilined message
|
||||
if (' ' == line[0])
|
||||
{
|
||||
line.erase(0, MULTI_LINE_PREFIX.length());
|
||||
append_to_last_message(messages, '\n' + line);
|
||||
}
|
||||
else if (0 == len && ('\n' == line[0] || '\r' == line[0]))
|
||||
{
|
||||
//to support old format's multilined messages with new lines used to divide paragraphs
|
||||
append_to_last_message(messages, line);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLSD item;
|
||||
if (!LLChatLogParser::parse(line, item, load_params))
|
||||
{
|
||||
item[LL_IM_TEXT] = line;
|
||||
}
|
||||
messages.push_back(item);
|
||||
}
|
||||
}
|
||||
fclose(fptr);
|
||||
}
|
||||
|
||||
void LLLogChat::startChatHistoryThread(const std::string& file_name, const LLSD& load_params)
|
||||
// static
|
||||
bool LLLogChat::historyThreadsFinished(LLUUID session_id)
|
||||
{
|
||||
|
||||
LLLoadHistoryThread* mThread = new LLLoadHistoryThread();
|
||||
mThread->start();
|
||||
mThread->setHistoryParams(file_name, load_params);
|
||||
LLMutexLock lock(historyThreadsMutex());
|
||||
bool finished = true;
|
||||
std::map<LLUUID,LLLoadHistoryThread *>::iterator it = sLoadHistoryThreads.find(session_id);
|
||||
if (it != sLoadHistoryThreads.end())
|
||||
{
|
||||
finished = it->second->isFinished();
|
||||
}
|
||||
if (!finished)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::map<LLUUID,LLDeleteHistoryThread *>::iterator dit = sDeleteHistoryThreads.find(session_id);
|
||||
if (dit != sDeleteHistoryThreads.end())
|
||||
{
|
||||
finished = finished && dit->second->isFinished();
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
|
||||
// static
|
||||
LLLoadHistoryThread* LLLogChat::getLoadHistoryThread(LLUUID session_id)
|
||||
{
|
||||
LLMutexLock lock(historyThreadsMutex());
|
||||
std::map<LLUUID,LLLoadHistoryThread *>::iterator it = sLoadHistoryThreads.find(session_id);
|
||||
if (it != sLoadHistoryThreads.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// static
|
||||
LLDeleteHistoryThread* LLLogChat::getDeleteHistoryThread(LLUUID session_id)
|
||||
{
|
||||
LLMutexLock lock(historyThreadsMutex());
|
||||
std::map<LLUUID,LLDeleteHistoryThread *>::iterator it = sDeleteHistoryThreads.find(session_id);
|
||||
if (it != sDeleteHistoryThreads.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// static
|
||||
bool LLLogChat::addLoadHistoryThread(LLUUID& session_id, LLLoadHistoryThread* lthread)
|
||||
{
|
||||
LLMutexLock lock(historyThreadsMutex());
|
||||
std::map<LLUUID,LLLoadHistoryThread *>::const_iterator it = sLoadHistoryThreads.find(session_id);
|
||||
if (it != sLoadHistoryThreads.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
sLoadHistoryThreads[session_id] = lthread;
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool LLLogChat::addDeleteHistoryThread(LLUUID& session_id, LLDeleteHistoryThread* dthread)
|
||||
{
|
||||
LLMutexLock lock(historyThreadsMutex());
|
||||
std::map<LLUUID,LLDeleteHistoryThread *>::const_iterator it = sDeleteHistoryThreads.find(session_id);
|
||||
if (it != sDeleteHistoryThreads.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
sDeleteHistoryThreads[session_id] = dthread;
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void LLLogChat::cleanupHistoryThreads()
|
||||
{
|
||||
LLMutexLock lock(historyThreadsMutex());
|
||||
std::vector<LLUUID> uuids;
|
||||
std::map<LLUUID,LLLoadHistoryThread *>::iterator lit = sLoadHistoryThreads.begin();
|
||||
for (; lit != sLoadHistoryThreads.end(); lit++)
|
||||
{
|
||||
if (lit->second->isFinished() && sDeleteHistoryThreads[lit->first]->isFinished())
|
||||
{
|
||||
delete lit->second;
|
||||
delete sDeleteHistoryThreads[lit->first];
|
||||
uuids.push_back(lit->first);
|
||||
}
|
||||
}
|
||||
std::vector<LLUUID>::iterator uuid_it = uuids.begin();
|
||||
for ( ;uuid_it != uuids.end(); uuid_it++)
|
||||
{
|
||||
sLoadHistoryThreads.erase(*uuid_it);
|
||||
sDeleteHistoryThreads.erase(*uuid_it);
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
LLMutex* LLLogChat::historyThreadsMutex()
|
||||
{
|
||||
if (sHistoryThreadsMutex == NULL)
|
||||
{
|
||||
sHistoryThreadsMutex = new LLMutex(NULL);
|
||||
}
|
||||
return sHistoryThreadsMutex;
|
||||
}
|
||||
|
||||
// static
|
||||
std::string LLLogChat::oldLogFileName(std::string filename)
|
||||
{
|
||||
|
|
@ -845,115 +945,188 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params
|
|||
return true; //parsed name and message text, maybe have a timestamp too
|
||||
}
|
||||
|
||||
LLDeleteHistoryThread::LLDeleteHistoryThread(std::list<LLSD>* messages, LLLoadHistoryThread* loadThread)
|
||||
: LLActionThread("delete chat history"),
|
||||
mMessages(messages),
|
||||
mLoadThread(loadThread)
|
||||
{
|
||||
}
|
||||
|
||||
LLDeleteHistoryThread::~LLDeleteHistoryThread()
|
||||
{
|
||||
}
|
||||
|
||||
LLLoadHistoryThread::LLLoadHistoryThread() : LLThread("load chat history")
|
||||
{
|
||||
mNewLoad = false;
|
||||
void LLDeleteHistoryThread::run()
|
||||
{
|
||||
if (mLoadThread != NULL)
|
||||
{
|
||||
mLoadThread->waitFinished();
|
||||
}
|
||||
if (NULL != mMessages)
|
||||
{
|
||||
delete mMessages;
|
||||
}
|
||||
mMessages = NULL;
|
||||
setFinished();
|
||||
}
|
||||
|
||||
LLActionThread::LLActionThread(const std::string& name)
|
||||
: LLThread(name),
|
||||
mMutex(NULL),
|
||||
mRunCondition(NULL),
|
||||
mFinished(false)
|
||||
{
|
||||
}
|
||||
|
||||
LLActionThread::~LLActionThread()
|
||||
{
|
||||
}
|
||||
|
||||
void LLActionThread::waitFinished()
|
||||
{
|
||||
mMutex.lock();
|
||||
if (!mFinished)
|
||||
{
|
||||
mMutex.unlock();
|
||||
mRunCondition.wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
mMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void LLActionThread::setFinished()
|
||||
{
|
||||
mMutex.lock();
|
||||
mFinished = true;
|
||||
mMutex.unlock();
|
||||
mRunCondition.signal();
|
||||
}
|
||||
|
||||
LLLoadHistoryThread::LLLoadHistoryThread(const std::string& file_name, std::list<LLSD>* messages, const LLSD& load_params)
|
||||
: LLActionThread("load chat history"),
|
||||
mMessages(messages),
|
||||
mFileName(file_name),
|
||||
mLoadParams(load_params),
|
||||
mNewLoad(true),
|
||||
mLoadEndSignal(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
LLLoadHistoryThread::~LLLoadHistoryThread()
|
||||
{
|
||||
}
|
||||
|
||||
void LLLoadHistoryThread::run()
|
||||
{
|
||||
if(mNewLoad)
|
||||
{
|
||||
loadHistory(mFileName, mMessages, mLoadParams);
|
||||
int count = mMessages->size();
|
||||
llinfos << "mMessages->size(): " << count << llendl;
|
||||
setFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void LLLoadHistoryThread::loadHistory(const std::string& file_name, std::list<LLSD>* messages, const LLSD& load_params)
|
||||
{
|
||||
if (file_name.empty())
|
||||
{
|
||||
LL_WARNS("LLLogChat::loadHistory") << "Session name is Empty!" << LL_ENDL;
|
||||
return ;
|
||||
}
|
||||
|
||||
void LLLoadHistoryThread::run()
|
||||
bool load_all_history = load_params.has("load_all_history") ? load_params["load_all_history"].asBoolean() : false;
|
||||
LLFILE* fptr = LLFile::fopen(LLLogChat::makeLogFileName(file_name), "r");/*Flawfinder: ignore*/
|
||||
|
||||
if (!fptr)
|
||||
{
|
||||
while (!LLApp::isQuitting())
|
||||
{
|
||||
if(mNewLoad)
|
||||
{
|
||||
loadHistory(mFileName,mMessages,mLoadParams);
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
fptr = LLFile::fopen(LLLogChat::oldLogFileName(file_name), "r");/*Flawfinder: ignore*/
|
||||
if (!fptr)
|
||||
{
|
||||
mNewLoad = false;
|
||||
(*mLoadEndSignal)(messages, file_name);
|
||||
return; //No previous conversation with this name.
|
||||
}
|
||||
}
|
||||
void LLLoadHistoryThread::setHistoryParams(const std::string& file_name, const LLSD& load_params)
|
||||
{
|
||||
mFileName = file_name;
|
||||
mLoadParams = load_params;
|
||||
mNewLoad = true;
|
||||
}
|
||||
void LLLoadHistoryThread::loadHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params)
|
||||
{
|
||||
|
||||
if (file_name.empty())
|
||||
{
|
||||
LL_WARNS("LLLogChat::loadHistory") << "Session name is Empty!" << LL_ENDL;
|
||||
return ;
|
||||
}
|
||||
char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/
|
||||
|
||||
bool load_all_history = load_params.has("load_all_history") ? load_params["load_all_history"].asBoolean() : false;
|
||||
char *bptr;
|
||||
S32 len;
|
||||
bool firstline = TRUE;
|
||||
|
||||
LLFILE* fptr = LLFile::fopen(LLLogChat::makeLogFileName(file_name), "r");/*Flawfinder: ignore*/
|
||||
if (!fptr)
|
||||
{
|
||||
fptr = LLFile::fopen(LLLogChat::oldLogFileName(file_name), "r");/*Flawfinder: ignore*/
|
||||
if (!fptr)
|
||||
{
|
||||
mNewLoad = false;
|
||||
(*mLoadEndSignal)(messages, file_name);
|
||||
return; //No previous conversation with this name.
|
||||
}
|
||||
}
|
||||
|
||||
char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/
|
||||
char *bptr;
|
||||
S32 len;
|
||||
bool firstline = TRUE;
|
||||
|
||||
if (load_all_history || fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END))
|
||||
{ //We need to load the whole historyFile or it's smaller than recall size, so get it all.
|
||||
firstline = FALSE;
|
||||
if (fseek(fptr, 0, SEEK_SET))
|
||||
{
|
||||
fclose(fptr);
|
||||
mNewLoad = false;
|
||||
(*mLoadEndSignal)(messages, file_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr))
|
||||
{
|
||||
len = strlen(buffer) - 1; /*Flawfinder: ignore*/
|
||||
for (bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0';
|
||||
|
||||
if (firstline)
|
||||
{
|
||||
firstline = FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string line(buffer);
|
||||
|
||||
//updated 1.23 plaint text log format requires a space added before subsequent lines in a multilined message
|
||||
if (' ' == line[0])
|
||||
{
|
||||
line.erase(0, MULTI_LINE_PREFIX.length());
|
||||
append_to_last_message(messages, '\n' + line);
|
||||
}
|
||||
else if (0 == len && ('\n' == line[0] || '\r' == line[0]))
|
||||
{
|
||||
//to support old format's multilined messages with new lines used to divide paragraphs
|
||||
append_to_last_message(messages, line);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLSD item;
|
||||
if (!LLChatLogParser::parse(line, item, load_params))
|
||||
{
|
||||
item[LL_IM_TEXT] = line;
|
||||
}
|
||||
messages.push_back(item);
|
||||
}
|
||||
}
|
||||
if (load_all_history || fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END))
|
||||
{ //We need to load the whole historyFile or it's smaller than recall size, so get it all.
|
||||
firstline = FALSE;
|
||||
if (fseek(fptr, 0, SEEK_SET))
|
||||
{
|
||||
fclose(fptr);
|
||||
mNewLoad = false;
|
||||
(*mLoadEndSignal)(messages, file_name);
|
||||
}
|
||||
|
||||
//static
|
||||
boost::signals2::connection LLLoadHistoryThread::setLoadEndSignal(const load_end_signal_t::slot_type& cb)
|
||||
{
|
||||
if (NULL == mLoadEndSignal)
|
||||
{
|
||||
mLoadEndSignal = new load_end_signal_t();
|
||||
return;
|
||||
}
|
||||
|
||||
return mLoadEndSignal->connect(cb);
|
||||
}
|
||||
|
||||
|
||||
while (fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr))
|
||||
{
|
||||
len = strlen(buffer) - 1; /*Flawfinder: ignore*/
|
||||
|
||||
for (bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0';
|
||||
|
||||
|
||||
if (firstline)
|
||||
{
|
||||
firstline = FALSE;
|
||||
continue;
|
||||
}
|
||||
std::string line(buffer);
|
||||
|
||||
//updated 1.23 plaint text log format requires a space added before subsequent lines in a multilined message
|
||||
if (' ' == line[0])
|
||||
{
|
||||
line.erase(0, MULTI_LINE_PREFIX.length());
|
||||
append_to_last_message(*messages, '\n' + line);
|
||||
}
|
||||
else if (0 == len && ('\n' == line[0] || '\r' == line[0]))
|
||||
{
|
||||
//to support old format's multilined messages with new lines used to divide paragraphs
|
||||
append_to_last_message(*messages, line);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLSD item;
|
||||
if (!LLChatLogParser::parse(line, item, load_params))
|
||||
{
|
||||
item[LL_IM_TEXT] = line;
|
||||
}
|
||||
messages->push_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fptr);
|
||||
mNewLoad = false;
|
||||
(*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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,23 +28,54 @@
|
|||
#define LL_LLLOGCHAT_H
|
||||
|
||||
class LLChat;
|
||||
class LLLoadHistoryThread : public LLThread
|
||||
|
||||
class LLActionThread : public LLThread
|
||||
{
|
||||
public:
|
||||
LLActionThread(const std::string& name);
|
||||
~LLActionThread();
|
||||
|
||||
void waitFinished();
|
||||
bool isFinished() { return mFinished; }
|
||||
protected:
|
||||
void setFinished();
|
||||
private:
|
||||
bool mFinished;
|
||||
LLMutex mMutex;
|
||||
LLCondition mRunCondition;
|
||||
};
|
||||
|
||||
class LLLoadHistoryThread : public LLActionThread
|
||||
{
|
||||
private:
|
||||
std::string mFileName;
|
||||
std::list<LLSD> mMessages;
|
||||
const std::string& mFileName;
|
||||
std::list<LLSD>* mMessages;
|
||||
LLSD mLoadParams;
|
||||
bool mNewLoad;
|
||||
public:
|
||||
LLLoadHistoryThread();
|
||||
|
||||
void setHistoryParams(const std::string& file_name, const LLSD& load_params);
|
||||
virtual void loadHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params);
|
||||
LLLoadHistoryThread(const std::string& file_name, std::list<LLSD>* messages, const LLSD& load_params);
|
||||
~LLLoadHistoryThread();
|
||||
//void setHistoryParams(const std::string& file_name, const LLSD& load_params);
|
||||
virtual void loadHistory(const std::string& file_name, std::list<LLSD>* messages, const LLSD& load_params);
|
||||
virtual void run();
|
||||
|
||||
typedef boost::signals2::signal<void (std::list<LLSD>& messages,const std::string& file_name)> load_end_signal_t;
|
||||
static load_end_signal_t * mLoadEndSignal;
|
||||
static boost::signals2::connection setLoadEndSignal(const load_end_signal_t::slot_type& cb);
|
||||
typedef boost::signals2::signal<void (std::list<LLSD>* messages,const std::string& file_name)> load_end_signal_t;
|
||||
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
|
||||
{
|
||||
private:
|
||||
std::list<LLSD>* mMessages;
|
||||
LLLoadHistoryThread* mLoadThread;
|
||||
public:
|
||||
LLDeleteHistoryThread(std::list<LLSD>* messages, LLLoadHistoryThread* loadThread);
|
||||
~LLDeleteHistoryThread();
|
||||
|
||||
virtual void run();
|
||||
static void deleteHistory();
|
||||
};
|
||||
|
||||
class LLLogChat
|
||||
|
|
@ -73,7 +104,6 @@ public:
|
|||
static void getListOfTranscriptBackupFiles(std::vector<std::string>& list_of_transcriptions);
|
||||
|
||||
static void loadChatHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params = LLSD());
|
||||
static void startChatHistoryThread(const std::string& file_name, const LLSD& load_params);
|
||||
|
||||
typedef boost::signals2::signal<void ()> save_history_signal_t;
|
||||
static boost::signals2::connection setSaveHistorySignal(const save_history_signal_t::slot_type& cb);
|
||||
|
|
@ -90,9 +120,21 @@ public:
|
|||
static bool isTranscriptExist(const LLUUID& avatar_id, bool is_group=false);
|
||||
static bool isNearbyTranscriptExist();
|
||||
|
||||
static bool historyThreadsFinished(LLUUID session_id);
|
||||
static LLLoadHistoryThread* getLoadHistoryThread(LLUUID session_id);
|
||||
static LLDeleteHistoryThread* getDeleteHistoryThread(LLUUID session_id);
|
||||
static bool addLoadHistoryThread(LLUUID& session_id, LLLoadHistoryThread* lthread);
|
||||
static bool addDeleteHistoryThread(LLUUID& session_id, LLDeleteHistoryThread* dthread);
|
||||
static void cleanupHistoryThreads();
|
||||
|
||||
private:
|
||||
static std::string cleanFileName(std::string filename);
|
||||
static save_history_signal_t * sSaveHistorySignal;
|
||||
|
||||
static std::map<LLUUID,LLLoadHistoryThread *> sLoadHistoryThreads;
|
||||
static std::map<LLUUID,LLDeleteHistoryThread *> sDeleteHistoryThreads;
|
||||
static LLMutex* sHistoryThreadsMutex;
|
||||
static LLMutex* historyThreadsMutex();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10224,4 +10224,14 @@ Cannot create large prims that intersect other players. Please re-try when othe
|
|||
yestext="OK"/>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="alert.tga"
|
||||
name="ChatHistoryIsBusyAlert"
|
||||
type="alertmodal">
|
||||
Chat history file is busy with previous operation. Please try again in a few minutes or choose chat with another person.
|
||||
<usetemplate
|
||||
name="okbutton"
|
||||
yestext="OK"/>
|
||||
</notification>
|
||||
|
||||
</notifications>
|
||||
|
|
|
|||
Loading…
Reference in New Issue