MAINT-3476 FIX Opening large chat histories from conversation log eats up huge amounts of memory, leading to viewer crash.

master
PavelK ProductEngine 2013-12-03 19:32:56 +02:00
parent 21d9e524f6
commit 443c502ccc
5 changed files with 491 additions and 203 deletions

View File

@ -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;

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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();
};
/**

View File

@ -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>