Allow calling LLNotificationsUtil::add from any thread.

This makes LLStringUtil thread-safe by removing a rather unnecessary
LLFastTimer from LLStringUtil::format.

Same thing for LLTrans::getString and LLTrans::findString, where
even a comment stated that the author wasn't interested in measuring
cpu time at all. In this case I added some code back to make sure
that we're not calling LLTrans::getString() in an inner loop, which
was the reason that the LLFastTimer was added.

Made one string static to avoid 45000 look ups during login, which
kinda triggered the above test.

Finally, LLNotificationsUtil::add is made thread-safe by making
LLNotificationChannelBase::mItems thread-safe and defering a call
to LLNotifications::updateItem to the main thread when called
from another thread (using a little statemachine).
This commit is contained in:
Aleric Inglewood
2013-11-15 17:52:52 +01:00
parent 5f9c6f1b08
commit a8cded0cf6
5 changed files with 167 additions and 24 deletions

View File

@@ -36,9 +36,6 @@
#include <winnls.h> // for WideCharToMultiByte
#endif
LLFastTimer::DeclareTimer FT_STRING_FORMAT("String Format");
std::string ll_safe_string(const char* in)
{
if(in) return std::string(in);
@@ -1190,7 +1187,6 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,
template<>
S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
{
LLFastTimer ft(FT_STRING_FORMAT);
S32 res = 0;
std::string output;
@@ -1263,7 +1259,6 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
template<>
S32 LLStringUtil::format(std::string& s, const LLSD& substitutions)
{
LLFastTimer ft(FT_STRING_FORMAT);
S32 res = 0;
if (!substitutions.isMap())

View File

@@ -41,6 +41,7 @@
#include "llnotifications.h"
#include "aialert.h"
#include "aistatemachine.h"
#include "../newview/hippogridmanager.h"
@@ -50,6 +51,9 @@
#endif
#include <boost/regex.hpp>
// Two macros, used to make access to mItems thread-safe, to keep the diff to a minimum.
#define AILOCK_mItems mItems_wat mItems_w(mItems_sf); LLNotificationSet& mItems(*mItems_w)
#define AILOCK_const_mItems mItems_crat mItems_r(mItems_sf); LLNotificationSet const& mItems(*mItems_r)
const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
@@ -99,6 +103,7 @@ private:
output["version"] = NOTIFICATION_PERSIST_VERSION;
LLSD& data = output["data"];
AILOCK_mItems;
for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
{
if (!LLNotificationTemplates::instance().templateExists((*it)->getName())) continue;
@@ -156,6 +161,7 @@ private:
void onDelete(LLNotificationPtr pNotification)
{
// we want to keep deleted notifications in our log
AILOCK_mItems;
mItems.insert(pNotification);
return;
@@ -757,6 +763,7 @@ void LLNotificationChannelBase::connectChanged(const LLStandardSignal::slot_type
// all of the notifications that are already in the channel
// we use a special signal called "load" in case the channel wants to care
// only about new notifications
AILOCK_mItems;
for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
{
slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
@@ -795,8 +802,11 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload)
// internal call, for use in avoiding lookup
bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPtr pNotification)
{
{
llassert(AIThreadID::in_main_thread());
std::string cmd = payload["sigtype"];
AILOCK_mItems;
LLNotificationSet::iterator foundItem = mItems.find(pNotification);
bool wasFound = (foundItem != mItems.end());
bool passesFilter = mFilter(pNotification);
@@ -943,6 +953,7 @@ void LLNotificationChannel::setComparator(LLNotificationComparator comparator)
{
mComparator = comparator;
LLNotificationSet s2(mComparator);
AILOCK_mItems;
s2.insert(mItems.begin(), mItems.end());
mItems.swap(s2);
@@ -952,16 +963,19 @@ void LLNotificationChannel::setComparator(LLNotificationComparator comparator)
bool LLNotificationChannel::isEmpty() const
{
AILOCK_const_mItems;
return mItems.empty();
}
LLNotificationChannel::Iterator LLNotificationChannel::begin()
{
AILOCK_const_mItems;
return mItems.begin();
}
LLNotificationChannel::Iterator LLNotificationChannel::end()
{
AILOCK_const_mItems;
return mItems.end();
}
@@ -1488,40 +1502,158 @@ LLNotificationPtr LLNotifications::add(AIAlert::Error const& error, int type, un
return add(LLNotification::Params((type == AIAlert::modal || error.is_modal()) ? "AIAlertModal" : "AIAlert").substitutions(substitutions));
}
//--------------------------------------------------------------------------------
// class UpdateItem
//
// Allow LLNotifications::add, LLNotifications::cancel and LLNotifications::update
// to be called from any thread.
struct UpdateItem
{
char const* sigtype;
LLNotificationPtr pNotif;
UpdateItem(char const* st, LLNotificationPtr const& np) : sigtype(st), pNotif(np) { }
void doit(void) const;
};
void UpdateItem::doit(void) const
{
LLNotifications::getInstance()->updateItem(LLSD().with("sigtype", sigtype).with("id", pNotif->id()), pNotif);
if (!strcmp(sigtype, "delete"))
{
pNotif->cancel();
}
}
class UpdateItemSM : public AIStateMachine
{
protected:
typedef AIStateMachine direct_base_type;
enum update_item_state_type {
UpdateItem_idle = direct_base_type::max_state,
UpdateItem_doit
};
public:
static state_type const max_state = UpdateItem_doit + 1;
public:
UpdateItemSM(void) : AIStateMachine(CWD_ONLY(true)) { }
static void add(UpdateItem const& ui);
private:
static UpdateItemSM* sSelf;
typedef std::deque<UpdateItem> updateQueue_type;
AIThreadSafeSimpleDC<updateQueue_type> mUpdateQueue;
typedef AIAccess<updateQueue_type> mUpdateQueue_wat;
typedef AIAccess<updateQueue_type> mUpdateQueue_rat;
typedef AIAccessConst<updateQueue_type> mUpdateQueue_crat;
protected:
/*virtual*/ ~UpdateItemSM() { }
protected:
/*virtual*/ void initialize_impl(void) { set_state(UpdateItem_idle); }
/*virtual*/ void multiplex_impl(state_type run_state);
/*virtual*/ void abort_impl(void) { }
/*virtual*/ void finish_impl(void) { }
/*virtual*/ char const* state_str_impl(state_type run_state) const;
};
//static
UpdateItemSM* UpdateItemSM::sSelf;
void UpdateItemSM::add(UpdateItem const& ui)
{
if (!sSelf)
{
sSelf = new UpdateItemSM;
sSelf->run(NULL, 0, false, true, &gMainThreadEngine);
}
if (AIThreadID::in_main_thread())
{
ui.doit();
return;
}
mUpdateQueue_wat mUpdateQueue_w(sSelf->mUpdateQueue);
mUpdateQueue_w->push_back(ui);
sSelf->advance_state(UpdateItem_doit);
}
char const* UpdateItemSM::state_str_impl(state_type run_state) const
{
switch(run_state)
{
// A complete listing of hello_world_state_type.
AI_CASE_RETURN(UpdateItem_idle);
AI_CASE_RETURN(UpdateItem_doit);
}
llassert(false);
return "UNKNOWN STATE";
}
void UpdateItemSM::multiplex_impl(state_type run_state)
{
switch(run_state)
{
case UpdateItem_idle:
idle();
break;
case UpdateItem_doit:
{
mUpdateQueue_wat mUpdateQueue_w(sSelf->mUpdateQueue);
while (!mUpdateQueue_w->empty())
{
UpdateItem const& ui(mUpdateQueue_w->front());
ui.doit();
mUpdateQueue_w->pop_front();
}
set_state(UpdateItem_idle);
break;
}
}
}
// end of UpdateItemSM
//--------------------------------------------------------------------------------
void LLNotifications::add(const LLNotificationPtr pNotif)
{
if (pNotif == NULL) return;
// first see if we already have it -- if so, that's a problem
AILOCK_mItems;
LLNotificationSet::iterator it=mItems.find(pNotif);
if (it != mItems.end())
{
llerrs << "Notification added a second time to the master notification channel." << llendl;
}
updateItem(LLSD().with("sigtype", "add").with("id", pNotif->id()), pNotif);
UpdateItemSM::add(UpdateItem("add", pNotif));
}
void LLNotifications::cancel(LLNotificationPtr pNotif)
{
if (pNotif == NULL || pNotif->isCancelled()) return;
AILOCK_mItems;
LLNotificationSet::iterator it=mItems.find(pNotif);
if (it == mItems.end())
{
llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl;
}
updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
pNotif->cancel();
UpdateItemSM::add(UpdateItem("delete", pNotif));
}
void LLNotifications::update(const LLNotificationPtr pNotif)
{
AILOCK_mItems;
LLNotificationSet::iterator it=mItems.find(pNotif);
if (it != mItems.end())
{
updateItem(LLSD().with("sigtype", "change").with("id", pNotif->id()), pNotif);
UpdateItemSM::add(UpdateItem("change", pNotif));
}
}
@@ -1529,6 +1661,7 @@ void LLNotifications::update(const LLNotificationPtr pNotif)
LLNotificationPtr LLNotifications::find(LLUUID const& uuid)
{
LLNotificationPtr target = LLNotificationPtr(new LLNotification(uuid));
AILOCK_mItems;
LLNotificationSet::iterator it=mItems.find(target);
if (it == mItems.end())
{
@@ -1543,6 +1676,7 @@ LLNotificationPtr LLNotifications::find(LLUUID const& uuid)
void LLNotifications::forEachNotification(NotificationProcess process)
{
AILOCK_mItems;
std::for_each(mItems.begin(), mItems.end(), process);
}

View File

@@ -107,6 +107,7 @@
#include "llxmlnode.h"
#include "llnotificationptr.h"
#include "llnotificationcontext.h"
#include "aithreadsafe.h"
namespace AIAlert { class Error; }
@@ -196,6 +197,7 @@ class LLNotification :
{
LOG_CLASS(LLNotification);
friend class LLNotifications;
friend class UpdateItem;
public:
// parameter object used to instantiate a new notification
@@ -566,9 +568,10 @@ class LLNotificationChannelBase :
public boost::signals2::trackable
{
LOG_CLASS(LLNotificationChannelBase);
friend class UpdateItem;
public:
LLNotificationChannelBase(LLNotificationFilter filter, LLNotificationComparator comp) :
mFilter(filter), mItems(comp)
mFilter(filter), mItems_sf(comp)
{}
virtual ~LLNotificationChannelBase() {}
// you can also connect to a Channel, so you can be notified of
@@ -582,7 +585,9 @@ public:
const LLNotificationFilter& getFilter() { return mFilter; }
protected:
LLNotificationSet mItems;
AIThreadSafeSimpleDC<LLNotificationSet> mItems_sf;
typedef AIAccess<LLNotificationSet> mItems_wat;
typedef AIAccessConst<LLNotificationSet> mItems_crat;
LLStandardSignal mChanged;
LLStandardSignal mPassedFilter;
LLStandardSignal mFailedFilter;

View File

@@ -91,15 +91,20 @@ bool LLTrans::parseStrings(const std::string& xml_filename, const std::set<std::
return true;
}
static LLFastTimer::DeclareTimer FTM_GET_TRANS("Translate string");
static LLAtomicU32 sStringTemplates_accesses;
int const access_increment = 1000;
static void log_sStringTemplates_accesses(void)
{
llinfos << "LLTrans::getString/findString called " << sStringTemplates_accesses << " in total." << llendl;
}
//static
std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args)
{
// Don't care about time as much as call count. Make sure we're not
// calling LLTrans::getString() in an inner loop. JC
LLFastTimer timer(FTM_GET_TRANS);
// Singu note: make sure LLTrans isn't used in a tight loop.
if (sStringTemplates_accesses++ % access_increment == access_increment - 1) log_sStringTemplates_accesses();
template_map_t::iterator iter = sStringTemplates.find(xml_desc);
if (iter != sStringTemplates.end())
@@ -125,9 +130,11 @@ std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::
//static
std::string LLTrans::getString(const std::string &xml_desc, const LLSD& msg_args)
{
// Don't care about time as much as call count. Make sure we're not
// calling LLTrans::getString() in an inner loop. JC
LLFastTimer timer(FTM_GET_TRANS);
// Singu note: make sure LLTrans isn't used in a tight loop.
if (sStringTemplates_accesses++ % access_increment == 0) log_sStringTemplates_accesses();
// Since sStringTemplates is read-only after it's initial initialization during start up,
// this function is already thread-safe.
template_map_t::iterator iter = sStringTemplates.find(xml_desc);
if (iter != sStringTemplates.end())
@@ -146,8 +153,9 @@ std::string LLTrans::getString(const std::string &xml_desc, const LLSD& msg_args
//static
bool LLTrans::findString(std::string &result, const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args)
{
LLFastTimer timer(FTM_GET_TRANS);
// Singu note: make sure LLTrans isn't used in a tight loop.
if (sStringTemplates_accesses++ % access_increment == 0) log_sStringTemplates_accesses();
template_map_t::iterator iter = sStringTemplates.find(xml_desc);
if (iter != sStringTemplates.end())
{
@@ -168,7 +176,8 @@ bool LLTrans::findString(std::string &result, const std::string &xml_desc, const
//static
bool LLTrans::findString(std::string &result, const std::string &xml_desc, const LLSD& msg_args)
{
//V3: LLFastTimer timer(FTM_GET_TRANS);
// Singu note: make sure LLTrans isn't used in a tight loop.
if (sStringTemplates_accesses++ % access_increment == 0) log_sStringTemplates_accesses();
template_map_t::iterator iter = sStringTemplates.find(xml_desc);
if (iter != sStringTemplates.end())

View File

@@ -1062,7 +1062,7 @@ void LLFolderViewItem::draw()
&& root_is_loading
&& mShowLoadStatus))
{
std::string load_string = " ( " + LLTrans::getString("LoadingData") + " ) ";
static std::string const load_string = " ( " + LLTrans::getString("LoadingData") + " ) ";
font->renderUTF8(load_string, 0, right_x, y, sSearchStatusColor,
LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
S32_MAX, S32_MAX, &right_x, FALSE);