From 9d5b897600a8f9405ec37a141b9417f34a11c557 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 2 Dec 2019 14:39:24 -0500 Subject: DRTVWR-494: Defend LLInstanceTracker against multi-thread usage. The previous implementation went to some effort to crash if anyone attempted to create or destroy an LLInstanceTracker subclass instance during traversal. That restriction is manageable within a single thread, but becomes unworkable if it's possible that a given subclass might be used on more than one thread. Remove LLInstanceTracker::instance_iter, beginInstances(), endInstances(), also key_iter, beginKeys() and endKeys(). Instead, introduce key_snapshot() and instance_snapshot(), the only means of iterating over LLInstanceTracker instances. (These are intended to resemble functions, but in fact the current implementation simply presents the classes.) Iterating over a captured snapshot defends against container modifications during traversal. The term 'snapshot' reminds the coder that a new instance created during traversal will not be considered. To defend against instance deletion during traversal, a snapshot stores std::weak_ptrs which it lazily dereferences, skipping on the fly any that have expired. Dereferencing instance_snapshot::iterator gets you a reference rather than a pointer. Because some use cases want to delete all existing instances, add an instance_snapshot::deleteAll() method that extracts the pointer. Those cases used to require explicitly copying instance pointers into a separate container; instance_snapshot() now takes care of that. It remains the caller's responsibility to ensure that all instances of that LLInstanceTracker subclass were allocated on the heap. Replace unkeyed static LLInstanceTracker::getInstance(T*) -- which returned nullptr if that instance had been destroyed -- with new getWeak() method returning std::weak_ptr. Caller must detect expiration of that weak_ptr. Adjust tests accordingly. Use of std::weak_ptr to detect expired instances requires engaging std::shared_ptr in the constructor. We now store shared_ptrs in the static containers (std::map for keyed, std::set for unkeyed). Make LLInstanceTrackerBase a template parameterized on the type of the static data it manages. For that reason, hoist static data class declarations out of the class definitions to an LLInstanceTrackerStuff namespace. Remove the static atomic sIterationNestDepth and its methods incrementDepth(), decrementDepth() and getDepth(), since they were used only to forbid creation and destruction during traversal. Add a std::mutex to static data. Introduce an internal LockStatic class that locks the mutex while providing a pointer to static data, making that the only way to access the static data. The LLINSTANCETRACKER_DTOR_NOEXCEPT macro goes away because we no longer expect ~LLInstanceTracker() to throw an exception in test programs. That affects LLTrace::StatBase as well as LLInstanceTracker itself. Adapt consumers to the new LLInstanceTracker API. --- indra/newview/llappviewer.cpp | 26 ++++-------------- indra/newview/llchathistory.cpp | 4 +-- indra/newview/llimprocessing.cpp | 4 +-- indra/newview/llscenemonitor.cpp | 44 +++++++++++++------------------ indra/newview/lltoast.cpp | 23 ++-------------- indra/newview/llviewercontrollistener.cpp | 12 +++------ 6 files changed, 31 insertions(+), 82 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index b232a8c3bb..a76ac58724 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1681,24 +1681,9 @@ bool LLAppViewer::cleanup() gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp"); } - { - // Kill off LLLeap objects. We can find them all because LLLeap is derived - // from LLInstanceTracker. But collect instances first: LLInstanceTracker - // specifically forbids adding/deleting instances while iterating. - std::vector leaps; - leaps.reserve(LLLeap::instanceCount()); - for (LLLeap::instance_iter li(LLLeap::beginInstances()), lend(LLLeap::endInstances()); - li != lend; ++li) - { - leaps.push_back(&*li); - } - // Okay, now trash them all. We don't have to NULL or erase the entry - // in 'leaps' because the whole vector is going away momentarily. - BOOST_FOREACH(LLLeap* leap, leaps) - { - delete leap; - } - } // destroy 'leaps' + // Kill off LLLeap objects. We can find them all because LLLeap is derived + // from LLInstanceTracker. + LLLeap::instance_snapshot().deleteAll(); //flag all elements as needing to be destroyed immediately // to ensure shutdown order @@ -2858,12 +2843,11 @@ bool LLAppViewer::initConfiguration() // Let anyone else who cares know that we've populated our settings // variables. - for (LLControlGroup::key_iter ki(LLControlGroup::beginKeys()), kend(LLControlGroup::endKeys()); - ki != kend; ++ki) + for (const auto& key : LLControlGroup::key_snapshot()) { // For each named instance of LLControlGroup, send an event saying // we've initialized an LLControlGroup instance by that name. - LLEventPumps::instance().obtain("LLControlGroup").post(LLSDMap("init", *ki)); + LLEventPumps::instance().obtain("LLControlGroup").post(LLSDMap("init", key)); } return true; // Config was successful. diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 1099d4bc09..4131af828e 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -1344,10 +1344,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL // We don't want multiple friendship offers to appear, this code checks if there are previous offers // by iterating though all panels. // Note: it might be better to simply add a "pending offer" flag somewhere - for (LLToastNotifyPanel::instance_iter ti(LLToastNotifyPanel::beginInstances()) - , tend(LLToastNotifyPanel::endInstances()); ti != tend; ++ti) + for (auto& panel : LLToastNotifyPanel::instance_snapshot()) { - LLToastNotifyPanel& panel = *ti; LLIMToastNotifyPanel * imtoastp = dynamic_cast(&panel); const std::string& notification_name = panel.getNotificationName(); if (notification_name == "OfferFriendship" diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index c3375a3779..c1dcc61010 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -1404,10 +1404,8 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, payload["sender"] = sender.getIPandPort(); bool add_notification = true; - for (LLToastNotifyPanel::instance_iter ti(LLToastNotifyPanel::beginInstances()) - , tend(LLToastNotifyPanel::endInstances()); ti != tend; ++ti) + for (auto& panel : LLToastNotifyPanel::instance_snapshot()) { - LLToastNotifyPanel& panel = *ti; const std::string& notification_name = panel.getNotificationName(); if (notification_name == "OfferFriendship" && panel.isControlPanelEnabled()) { diff --git a/indra/newview/llscenemonitor.cpp b/indra/newview/llscenemonitor.cpp index 5ab0013055..2c0c38dc75 100644 --- a/indra/newview/llscenemonitor.cpp +++ b/indra/newview/llscenemonitor.cpp @@ -559,16 +559,14 @@ void LLSceneMonitor::dumpToFile(std::string file_name) typedef StatType trace_count; - for (trace_count::instance_iter it = trace_count::beginInstances(), end_it = trace_count::endInstances(); - it != end_it; - ++it) + for (auto& it : trace_count::instance_snapshot()) { std::ostringstream row; row << std::setprecision(10); - row << it->getName(); + row << it.getName(); - const char* unit_label = it->getUnitLabel(); + const char* unit_label = it.getUnitLabel(); if(unit_label[0]) { row << "(" << unit_label << ")"; @@ -579,8 +577,8 @@ void LLSceneMonitor::dumpToFile(std::string file_name) for (S32 frame = 1; frame <= frame_count; frame++) { Recording& recording = scene_load_recording.getPrevRecording(frame_count - frame); - samples += recording.getSampleCount(*it); - row << ", " << recording.getSum(*it); + samples += recording.getSampleCount(it); + row << ", " << recording.getSum(it); } row << '\n'; @@ -593,15 +591,13 @@ void LLSceneMonitor::dumpToFile(std::string file_name) typedef StatType trace_event; - for (trace_event::instance_iter it = trace_event::beginInstances(), end_it = trace_event::endInstances(); - it != end_it; - ++it) + for (auto& it : trace_event::instance_snapshot()) { std::ostringstream row; row << std::setprecision(10); - row << it->getName(); + row << it.getName(); - const char* unit_label = it->getUnitLabel(); + const char* unit_label = it.getUnitLabel(); if(unit_label[0]) { row << "(" << unit_label << ")"; @@ -612,8 +608,8 @@ void LLSceneMonitor::dumpToFile(std::string file_name) for (S32 frame = 1; frame <= frame_count; frame++) { Recording& recording = scene_load_recording.getPrevRecording(frame_count - frame); - samples += recording.getSampleCount(*it); - F64 mean = recording.getMean(*it); + samples += recording.getSampleCount(it); + F64 mean = recording.getMean(it); if (llisnan(mean)) { row << ", n/a"; @@ -634,15 +630,13 @@ void LLSceneMonitor::dumpToFile(std::string file_name) typedef StatType trace_sample; - for (trace_sample::instance_iter it = trace_sample::beginInstances(), end_it = trace_sample::endInstances(); - it != end_it; - ++it) + for (auto& it : trace_sample::instance_snapshot()) { std::ostringstream row; row << std::setprecision(10); - row << it->getName(); + row << it.getName(); - const char* unit_label = it->getUnitLabel(); + const char* unit_label = it.getUnitLabel(); if(unit_label[0]) { row << "(" << unit_label << ")"; @@ -653,8 +647,8 @@ void LLSceneMonitor::dumpToFile(std::string file_name) for (S32 frame = 1; frame <= frame_count; frame++) { Recording& recording = scene_load_recording.getPrevRecording(frame_count - frame); - samples += recording.getSampleCount(*it); - F64 mean = recording.getMean(*it); + samples += recording.getSampleCount(it); + F64 mean = recording.getMean(it); if (llisnan(mean)) { row << ", n/a"; @@ -674,15 +668,13 @@ void LLSceneMonitor::dumpToFile(std::string file_name) } typedef StatType trace_mem; - for (trace_mem::instance_iter it = trace_mem::beginInstances(), end_it = trace_mem::endInstances(); - it != end_it; - ++it) + for (auto& it : trace_mem::instance_snapshot()) { - os << it->getName() << "(KiB)"; + os << it.getName() << "(KiB)"; for (S32 frame = 1; frame <= frame_count; frame++) { - os << ", " << scene_load_recording.getPrevRecording(frame_count - frame).getMax(*it).valueInUnits(); + os << ", " << scene_load_recording.getPrevRecording(frame_count - frame).getMax(it).valueInUnits(); } os << '\n'; diff --git a/indra/newview/lltoast.cpp b/indra/newview/lltoast.cpp index 870e0d94f0..bf56a10d4d 100644 --- a/indra/newview/lltoast.cpp +++ b/indra/newview/lltoast.cpp @@ -612,11 +612,8 @@ S32 LLToast::notifyParent(const LLSD& info) //static void LLToast::updateClass() { - for (LLInstanceTracker::instance_iter iter = LLInstanceTracker::beginInstances(); - iter != LLInstanceTracker::endInstances(); ) + for (auto& toast : LLInstanceTracker::instance_snapshot()) { - LLToast& toast = *iter++; - toast.updateHoveredState(); } } @@ -624,22 +621,6 @@ void LLToast::updateClass() // static void LLToast::cleanupToasts() { - LLToast * toastp = NULL; - - while (LLInstanceTracker::instanceCount() > 0) - { - { // Need to scope iter to allow deletion - LLInstanceTracker::instance_iter iter = LLInstanceTracker::beginInstances(); - toastp = &(*iter); - } - - //LL_INFOS() << "Cleaning up toast id " << toastp->getNotificationID() << LL_ENDL; - - // LLToast destructor will remove it from the LLInstanceTracker. - if (!toastp) - break; // Don't get stuck in the loop if a null pointer somehow got on the list - - delete toastp; - } + LLInstanceTracker::instance_snapshot().deleteAll(); } diff --git a/indra/newview/llviewercontrollistener.cpp b/indra/newview/llviewercontrollistener.cpp index d2484b2b23..3443bb644a 100644 --- a/indra/newview/llviewercontrollistener.cpp +++ b/indra/newview/llviewercontrollistener.cpp @@ -50,11 +50,9 @@ LLViewerControlListener::LLViewerControlListener() std::ostringstream groupnames; groupnames << "[\"group\"] is one of "; const char* delim = ""; - for (LLControlGroup::key_iter cgki(LLControlGroup::beginKeys()), - cgkend(LLControlGroup::endKeys()); - cgki != cgkend; ++cgki) + for (const auto& key : LLControlGroup::key_snapshot()) { - groupnames << delim << '"' << *cgki << '"'; + groupnames << delim << '"' << key << '"'; delim = ", "; } groupnames << '\n'; @@ -181,11 +179,9 @@ void LLViewerControlListener::groups(LLSD const & request) { // No Info, we're not looking up either a group or a control name. Response response(LLSD(), request); - for (LLControlGroup::key_iter cgki(LLControlGroup::beginKeys()), - cgkend(LLControlGroup::endKeys()); - cgki != cgkend; ++cgki) + for (const auto& key : LLControlGroup::key_snapshot()) { - response["groups"].append(*cgki); + response["groups"].append(key); } } -- cgit v1.3 From 47ec6ab3be5df5ee3f80a642d9c2ef7f4dac0d8a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 30 May 2019 08:23:32 -0400 Subject: SL-11216: Remove LLSingletonBase::cleanupAll(). Remove call from LLAppViewer::cleanup(). Instead, make each LLSingleton::deleteSingleton() call cleanupSingleton() just before destroying the instance. Since deleteSingleton() is not a destructor, it's fine to call cleanupSingleton() from there; and since deleteAll() calls deleteSingleton() on every remaining instance, the former cleanupAll() functionality has been subsumed into deleteAll(). Since cleanupSingleton() is now called at exactly one point in the instance's lifetime, we no longer need a bool indicating whether it has been called. The previous protocol of calling cleanupAll() before deleteAll() implemented a two-phase cleanup strategy for the application. That is no longer needed. Moreover, the cleanupAll() / deleteAll() sequence created a time window during which individual LLSingleton instances weren't usable (to the extent that their cleanupSingleton() methods released essential resources) but still existed -- so a getInstance() call would return the crippled instance rather than recreating it. Remove cleanupAll() calls from tests; adjust to new order of expected side effects: instead of A::cleanupSingleton(), B::cleanupSingleton(), ~A(), ~B(), now we get A::cleanupSingleton(), ~A(), B::cleanupSingleton(), ~B(). --- indra/llcommon/llsingleton.cpp | 35 +------------- indra/llcommon/llsingleton.h | 105 +++++++++++++++-------------------------- indra/newview/llappviewer.cpp | 16 ++----- 3 files changed, 44 insertions(+), 112 deletions(-) (limited to 'indra/newview') diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index 4c76206d8d..f5f3aec270 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -378,8 +378,7 @@ LLSingletonBase::vec_t LLSingletonBase::dep_sort() // SingletonDeps through the life of the program, dynamically adding and // removing LLSingletons as they are created and destroyed, in practice // it's less messy to construct it on demand. The overhead of doing so - // should happen basically twice: once for cleanupAll(), once for - // deleteAll(). + // should happen basically once: for deleteAll(). typedef LLDependencies SingletonDeps; SingletonDeps sdeps; // Lock while traversing the master list @@ -412,38 +411,6 @@ LLSingletonBase::vec_t LLSingletonBase::dep_sort() return ret; } -//static -void LLSingletonBase::cleanupAll() -{ - // It's essential to traverse these in dependency order. - BOOST_FOREACH(LLSingletonBase* sp, dep_sort()) - { - // Call cleanupSingleton() only if we haven't already done so for this - // instance. - if (! sp->mCleaned) - { - sp->mCleaned = true; - - logdebugs("calling ", - classname(sp).c_str(), "::cleanupSingleton()"); - try - { - sp->cleanupSingleton(); - } - catch (const std::exception& e) - { - logwarns("Exception in ", classname(sp).c_str(), - "::cleanupSingleton(): ", e.what()); - } - catch (...) - { - logwarns("Unknown exception in ", classname(sp).c_str(), - "::cleanupSingleton()"); - } - } - } -} - void LLSingletonBase::cleanup_() { logdebugs("calling ", classname(this).c_str(), "::cleanupSingleton()"); diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 5ee40a658a..65dd332afb 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -50,7 +50,6 @@ private: typedef std::vector vec_t; static vec_t dep_sort(); - bool mCleaned; // cleanupSingleton() has been called // we directly depend on these other LLSingletons typedef boost::unordered_set set_t; set_t mDepends; @@ -142,32 +141,15 @@ protected: public: /** - * Call this to call the cleanupSingleton() method for every LLSingleton - * constructed since the start of the last cleanupAll() call. (Any - * LLSingleton constructed DURING a cleanupAll() call won't be cleaned up - * until the next cleanupAll() call.) cleanupSingleton() neither deletes - * nor destroys its LLSingleton; therefore it's safe to include logic that - * might take significant realtime or even throw an exception. - * - * The most important property of cleanupAll() is that cleanupSingleton() - * methods are called in dependency order, leaf classes last. Thus, given - * two LLSingleton subclasses A and B, if A's dependency on B is properly - * expressed as a B::getInstance() or B::instance() call during either - * A::A() or A::initSingleton(), B will be cleaned up after A. - * - * If a cleanupSingleton() method throws an exception, the exception is - * logged, but cleanupAll() attempts to continue calling the rest of the - * cleanupSingleton() methods. - */ - static void cleanupAll(); - /** - * Call this to call the deleteSingleton() method for every LLSingleton - * constructed since the start of the last deleteAll() call. (Any - * LLSingleton constructed DURING a deleteAll() call won't be cleaned up - * until the next deleteAll() call.) deleteSingleton() deletes and - * destroys its LLSingleton. Any cleanup logic that might take significant - * realtime -- or throw an exception -- must not be placed in your - * LLSingleton's destructor, but rather in its cleanupSingleton() method. + * deleteAll() calls the cleanupSingleton() and deleteSingleton() methods + * for every LLSingleton constructed since the start of the last + * deleteAll() call. (Any LLSingleton constructed DURING a deleteAll() + * call won't be cleaned up until the next deleteAll() call.) + * deleteSingleton() deletes and destroys its LLSingleton. Any cleanup + * logic that might take significant realtime -- or throw an exception -- + * must not be placed in your LLSingleton's destructor, but rather in its + * cleanupSingleton() method, which is called implicitly by + * deleteSingleton(). * * The most important property of deleteAll() is that deleteSingleton() * methods are called in dependency order, leaf classes last. Thus, given @@ -175,9 +157,9 @@ public: * expressed as a B::getInstance() or B::instance() call during either * A::A() or A::initSingleton(), B will be cleaned up after A. * - * If a deleteSingleton() method throws an exception, the exception is - * logged, but deleteAll() attempts to continue calling the rest of the - * deleteSingleton() methods. + * If a cleanupSingleton() or deleteSingleton() method throws an + * exception, the exception is logged, but deleteAll() attempts to + * continue calling the rest of the deleteSingleton() methods. */ static void deleteAll(); }; @@ -226,7 +208,6 @@ struct LLSingleton_manage_master // Now we can implement LLSingletonBase's template constructor. template LLSingletonBase::LLSingletonBase(tag): - mCleaned(false), mDeleteSingleton(nullptr) { // This is the earliest possible point at which we can push this new @@ -269,10 +250,19 @@ class LLParamSingleton; * leading back to yours, move the instance reference from your constructor to * your initSingleton() method. * - * If you override LLSingleton::cleanupSingleton(), your method will be - * called if someone calls LLSingletonBase::cleanupAll(). The significant part - * of this promise is that cleanupAll() will call individual - * cleanupSingleton() methods in reverse dependency order. + * If you override LLSingleton::cleanupSingleton(), your method will + * implicitly be called by LLSingleton::deleteSingleton() just before the + * instance is destroyed. We introduce a special cleanupSingleton() method + * because cleanupSingleton() operations can involve nontrivial realtime, or + * throw an exception. A destructor should do neither! + * + * If your cleanupSingleton() method throws an exception, we log that + * exception but carry on. + * + * If at some point you call LLSingletonBase::deleteAll(), all remaining + * LLSingleton instances will be destroyed in reverse dependency order. (Or + * call MySubclass::deleteSingleton() to specifically destroy the canonical + * MySubclass instance.) * * That is, consider LLSingleton subclasses C, B and A. A depends on B, which * in turn depends on C. These dependencies are expressed as calls to @@ -280,26 +270,14 @@ class LLParamSingleton; * It shouldn't matter whether these calls appear in A::A() or * A::initSingleton(), likewise B::B() or B::initSingleton(). * - * We promise that if you later call LLSingletonBase::cleanupAll(): - * 1. A::cleanupSingleton() will be called before - * 2. B::cleanupSingleton(), which will be called before - * 3. C::cleanupSingleton(). + * We promise that if you later call LLSingletonBase::deleteAll(): + * 1. A::deleteSingleton() will be called before + * 2. B::deleteSingleton(), which will be called before + * 3. C::deleteSingleton(). * Put differently, if your LLSingleton subclass constructor or * initSingleton() method explicitly depends on some other LLSingleton * subclass, you may continue to rely on that other subclass in your * cleanupSingleton() method. - * - * We introduce a special cleanupSingleton() method because cleanupSingleton() - * operations can involve nontrivial realtime, or might throw an exception. A - * destructor should do neither! - * - * If your cleanupSingleton() method throws an exception, we log that - * exception but proceed with the remaining cleanupSingleton() calls. - * - * Similarly, if at some point you call LLSingletonBase::deleteAll(), all - * remaining LLSingleton instances will be destroyed in dependency order. (Or - * call MySubclass::deleteSingleton() to specifically destroy the canonical - * MySubclass instance.) */ template class LLSingleton : public LLSingletonBase @@ -445,25 +423,18 @@ protected: public: /** - * @brief Immediately delete the singleton. + * @brief Cleanup and destroy the singleton instance. * - * A subsequent call to LLProxy::getInstance() will construct a new - * instance of the class. + * deleteSingleton() calls this instance's cleanupSingleton() method and + * then destroys the instance. * - * Without an explicit call to LLSingletonBase::deleteAll(), LLSingletons - * are implicitly destroyed after main() has exited and the C++ runtime is - * cleaning up statically-constructed objects. Some classes derived from - * LLSingleton have objects that are part of a runtime system that is - * terminated before main() exits. Calling the destructor of those objects - * after the termination of their respective systems can cause crashes and - * other problems during termination of the project. Using this method to - * destroy the singleton early can prevent these crashes. + * A subsequent call to LLSingleton::getInstance() will construct a new + * instance of the class. * - * An example where this is needed is for a LLSingleton that has an APR - * object as a member that makes APR calls on destruction. The APR system is - * shut down explicitly before main() exits. This causes a crash on exit. - * Using this method before the call to apr_terminate() and NOT calling - * getInstance() again will prevent the crash. + * Without an explicit call to LLSingletonBase::deleteAll(), or + * LLSingleton::deleteSingleton(), LLSingleton instances are simply + * leaked. (Allowing implicit destruction at shutdown caused too many + * problems.) */ static void deleteSingleton() { diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index a76ac58724..ed80e7c0ce 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2093,25 +2093,19 @@ bool LLAppViewer::cleanup() removeMarkerFiles(); - // It's not at first obvious where, in this long sequence, generic cleanup - // calls OUGHT to go. So let's say this: as we migrate cleanup from + // It's not at first obvious where, in this long sequence, a generic cleanup + // call OUGHT to go. So let's say this: as we migrate cleanup from // explicit hand-placed calls into the generic mechanism, eventually - // all cleanup will get subsumed into the generic calls. So the calls you + // all cleanup will get subsumed into the generic call. So the calls you // still see above are calls that MUST happen before the generic cleanup // kicks in. - // This calls every remaining LLSingleton's cleanupSingleton() method. - // This method should perform any cleanup that might take significant - // realtime, or might throw an exception. - LLSingletonBase::cleanupAll(); - // The logging subsystem depends on an LLSingleton. Any logging after // LLSingletonBase::deleteAll() won't be recorded. LL_INFOS() << "Goodbye!" << LL_ENDL; - // This calls every remaining LLSingleton's deleteSingleton() method. - // No class destructor should perform any cleanup that might take - // significant realtime, or throw an exception. + // This calls every remaining LLSingleton's cleanupSingleton() and + // deleteSingleton() methods. LLSingletonBase::deleteAll(); removeDumpDir(); -- cgit v1.3 From 5a260e0cc3beec45da1d29578855524977206022 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 30 May 2019 10:39:37 -0400 Subject: SL-11216: Convert LLVersionInfo to an LLSingleton. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changeset is meant to exemplify how to convert a "namespace" class whose methods are static -- and whose data are module-static -- to an LLSingleton. LLVersionInfo has no initClass() or cleanupClass() methods, but the general idea is the same. * Derive the class from LLSingleton: class LLSomeSingleton: public LLSingleton { ... }; * Add LLSINGLETON(LLSomeSingleton); in the private section of the class. This usage implies a separate LLSomeSingleton::LLSomeSingleton() definition, as described in indra/llcommon/llsingleton.h. * Move module-scope data in the .cpp file to non-static class members. Change any sVariableName to mVariableName to avoid being outright misleading. * Make static class methods non-static. Remove '//static' comments from method definitions as needed. * For LLVersionInfo specifically, the 'const std::string&' return type was replaced with 'std::string'. Returning a reference to a static or a member, const or otherwise, is an anti-pattern: the interface constrains the implementation, prohibiting possibly later returning a temporary (an expression). * For LLVersionInfo specifically, 'const S32' return type was replaced with simple 'S32'. 'const' is just noise in that usage. * Simple member initialization (e.g. the original initializer expressions for static variables) can be done with member{ value } initializers (no examples here though). * Delete initClass() method. * LLSingleton's forté is of course lazy initialization. It might work to simply delete any calls to initClass(). But if there are side effects that must happen at that moment, replace LLSomeSingleton::initClass() with (void)LLSomeSingleton::instance(); * Most initClass() initialization can be done in the constructor, as would normally be the case. * Initialization that might cause a circular LLSingleton reference should be moved to initSingleton(). Override 'void initSingleton();' should be private. * For LLVersionInfo specifically, certain initialization that used to be lazily performed was made unconditional, due to its low cost. * For LLVersionInfo specifically, certain initialization involved calling methods that have become non-static. This was moved to initSingleton() because, in a constructor body, 'this' does not yet point to the enclosing class. * Delete cleanupClass() method. * There is already a generic LLSingletonBase::deleteAll() call in LLAppViewer::cleanup(). It might work to let this new LLSingleton be cleaned up with all the rest. But if there are side effects that must happen at that moment, replace LLSomeSingleton::cleanupClass() with LLSomeSingleton::deleteSingleton(). That said, much of the benefit of converting to LLSingleton is deleteAll()'s guarantee that cross-LLSingleton dependencies will be properly honored: we're trying to migrate the code base away from the present fragile manual cleanup sequence. * Most cleanupClass() cleanup can be done in the destructor, as would normally be the case. * Cleanup that might throw an exception should be moved to cleanupSingleton(). Override 'void cleanupSingleton();' should be private. * Within LLSomeSingleton methods, remove any existing LLSomeSingleton::methodName() qualification: simple methodName() is better. * In the rest of the code base, convert most LLSomeSingleton::methodName() references to LLSomeSingleton::instance().methodName(). (Prefer instance() to getInstance() because a reference does not admit the possibility of NULL.) * Of course, LLSomeSingleton::ENUM_VALUE can remain unchanged. In general, for many successive references to an LLSingleton instance, it can be useful to capture the instance() as in: auto& versionInfo{LLVersionInfo::instance()}; // ... versionInfo.getVersion() ... We did not do that here only to simplify the code review. The STRINGIZE(expression) macro encapsulates: std::ostringstream out; out << expression; return out.str(); We used that in a couple places. For LLVersionInfo specifically, lllogininstance_test.cpp used to dummy out a couple specific static methods. It's harder to dummy out LLSingleton::instance() references, so we add the real class to that test. --- indra/newview/CMakeLists.txt | 1 + indra/newview/llappviewer.cpp | 50 ++++++++-------- indra/newview/llcurrencyuimanager.cpp | 20 +++---- indra/newview/llfloaterreporter.cpp | 4 +- indra/newview/lllogininstance.cpp | 4 +- indra/newview/llpanellogin.cpp | 12 ++-- indra/newview/llstartup.cpp | 10 ++-- indra/newview/lltexturestats.cpp | 4 +- indra/newview/lltranslate.cpp | 20 +++---- indra/newview/llversioninfo.cpp | 89 +++++++++++----------------- indra/newview/llversioninfo.h | 47 ++++++++++----- indra/newview/llviewermedia.cpp | 4 +- indra/newview/llviewerstats.cpp | 2 +- indra/newview/llviewerwindow.cpp | 2 +- indra/newview/llvoicevivox.cpp | 2 +- indra/newview/llweb.cpp | 12 ++-- indra/newview/tests/lllogininstance_test.cpp | 2 - indra/newview/tests/llversioninfo_test.cpp | 24 ++++---- 18 files changed, 152 insertions(+), 157 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 800696825f..dc0d737540 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2434,6 +2434,7 @@ if (LL_TESTS) set_source_files_properties( lllogininstance.cpp PROPERTIES + LL_TEST_ADDITIONAL_SOURCE_FILES llversioninfo.cpp LL_TEST_ADDITIONAL_LIBRARIES "${BOOST_SYSTEM_LIBRARY}" ) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ed80e7c0ce..795d14740a 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1120,7 +1120,7 @@ bool LLAppViewer::init() // Save the current version to the prefs file gSavedSettings.setString("LastRunVersion", - LLVersionInfo::getChannelAndVersion()); + LLVersionInfo::instance().getChannelAndVersion()); gSimLastTime = gRenderStartTime.getElapsedTimeF32(); gSimFrames = (F32)gFrameCount; @@ -1156,7 +1156,7 @@ bool LLAppViewer::init() // UpdaterServiceSettings updater.args.add(stringize(gSavedSettings.getU32("UpdaterServiceSetting"))); // channel - updater.args.add(LLVersionInfo::getChannel()); + updater.args.add(LLVersionInfo::instance().getChannel()); // testok updater.args.add(stringize(gSavedSettings.getBOOL("UpdaterWillingToTest"))); // ForceAddressSize @@ -2616,7 +2616,7 @@ bool LLAppViewer::initConfiguration() std::string CmdLineChannel(gSavedSettings.getString("CmdLineChannel")); if(! CmdLineChannel.empty()) { - LLVersionInfo::resetChannel(CmdLineChannel); + LLVersionInfo::instance().resetChannel(CmdLineChannel); } // If we have specified crash on startup, set the global so we'll trigger the crash at the right time @@ -3062,15 +3062,15 @@ LLSD LLAppViewer::getViewerInfo() const // LLFloaterAbout. LLSD info; LLSD version; - version.append(LLVersionInfo::getMajor()); - version.append(LLVersionInfo::getMinor()); - version.append(LLVersionInfo::getPatch()); - version.append(LLVersionInfo::getBuild()); + version.append(LLVersionInfo::instance().getMajor()); + version.append(LLVersionInfo::instance().getMinor()); + version.append(LLVersionInfo::instance().getPatch()); + version.append(LLVersionInfo::instance().getBuild()); info["VIEWER_VERSION"] = version; - info["VIEWER_VERSION_STR"] = LLVersionInfo::getVersion(); - info["CHANNEL"] = LLVersionInfo::getChannel(); + info["VIEWER_VERSION_STR"] = LLVersionInfo::instance().getVersion(); + info["CHANNEL"] = LLVersionInfo::instance().getChannel(); info["ADDRESS_SIZE"] = ADDRESS_SIZE; - std::string build_config = LLVersionInfo::getBuildConfig(); + std::string build_config = LLVersionInfo::instance().getBuildConfig(); if (build_config != "Release") { info["BUILD_CONFIG"] = build_config; @@ -3081,7 +3081,7 @@ LLSD LLAppViewer::getViewerInfo() const std::string url = LLTrans::getString("RELEASE_NOTES_BASE_URL"); if (! LLStringUtil::endsWith(url, "/")) url += "/"; - url += LLURI::escape(LLVersionInfo::getVersion()) + ".html"; + url += LLURI::escape(LLVersionInfo::instance().getVersion()) + ".html"; info["VIEWER_RELEASE_NOTES_URL"] = url; @@ -3372,12 +3372,12 @@ void LLAppViewer::writeSystemInfo() gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old"); //LLError::logFileName(); #endif - gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); - gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); - gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); - gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); - gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::getBuild(); - gDebugInfo["ClientInfo"]["AddressSize"] = LLVersionInfo::getAddressSize(); + gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::instance().getChannel(); + gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::instance().getMajor(); + gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::instance().getMinor(); + gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::instance().getPatch(); + gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::instance().getBuild(); + gDebugInfo["ClientInfo"]["AddressSize"] = LLVersionInfo::instance().getAddressSize(); gDebugInfo["CAFilename"] = gDirUtilp->getCAFile(); @@ -3420,7 +3420,7 @@ void LLAppViewer::writeSystemInfo() // Dump some debugging info LL_INFOS("SystemInfo") << "Application: " << LLTrans::getString("APP_NAME") << LL_ENDL; - LL_INFOS("SystemInfo") << "Version: " << LLVersionInfo::getChannelAndVersion() << LL_ENDL; + LL_INFOS("SystemInfo") << "Version: " << LLVersionInfo::instance().getChannelAndVersion() << LL_ENDL; // Dump the local time and time zone time_t now; @@ -3642,7 +3642,7 @@ void LLAppViewer::handleViewerCrash() // static void LLAppViewer::recordMarkerVersion(LLAPRFile& marker_file) { - std::string marker_version(LLVersionInfo::getChannelAndVersion()); + std::string marker_version(LLVersionInfo::instance().getChannelAndVersion()); if ( marker_version.length() > MAX_MARKER_LENGTH ) { LL_WARNS_ONCE("MarkerFile") << "Version length ("<< marker_version.length()<< ")" @@ -3659,7 +3659,7 @@ bool LLAppViewer::markerIsSameVersion(const std::string& marker_name) const { bool sameVersion = false; - std::string my_version(LLVersionInfo::getChannelAndVersion()); + std::string my_version(LLVersionInfo::instance().getChannelAndVersion()); char marker_version[MAX_MARKER_LENGTH]; S32 marker_version_length; @@ -5517,12 +5517,12 @@ void LLAppViewer::handleLoginComplete() initMainloopTimeout("Mainloop Init"); // Store some data to DebugInfo in case of a freeze. - gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); + gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::instance().getChannel(); - gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); - gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); - gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); - gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::getBuild(); + gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::instance().getMajor(); + gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::instance().getMinor(); + gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::instance().getPatch(); + gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::instance().getBuild(); LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); if ( parcel && parcel->getMusicURL()[0]) diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index b4a1457f47..df94e337da 100644 --- a/indra/newview/llcurrencyuimanager.cpp +++ b/indra/newview/llcurrencyuimanager.cpp @@ -166,11 +166,11 @@ void LLCurrencyUIManager::Impl::updateCurrencyInfo() gAgent.getSecureSessionID().asString()); keywordArgs.appendString("language", LLUI::getLanguage()); keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy); - keywordArgs.appendString("viewerChannel", LLVersionInfo::getChannel()); - keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::getMajor()); - keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::getMinor()); - keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::getPatch()); - keywordArgs.appendInt("viewerBuildVersion", LLVersionInfo::getBuild()); + keywordArgs.appendString("viewerChannel", LLVersionInfo::instance().getChannel()); + keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::instance().getMajor()); + keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::instance().getMinor()); + keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::instance().getPatch()); + keywordArgs.appendInt("viewerBuildVersion", LLVersionInfo::instance().getBuild()); LLXMLRPCValue params = LLXMLRPCValue::createArray(); params.append(keywordArgs); @@ -241,11 +241,11 @@ void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password) { keywordArgs.appendString("password", password); } - keywordArgs.appendString("viewerChannel", LLVersionInfo::getChannel()); - keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::getMajor()); - keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::getMinor()); - keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::getPatch()); - keywordArgs.appendInt("viewerBuildVersion", LLVersionInfo::getBuild()); + keywordArgs.appendString("viewerChannel", LLVersionInfo::instance().getChannel()); + keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::instance().getMajor()); + keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::instance().getMinor()); + keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::instance().getPatch()); + keywordArgs.appendInt("viewerBuildVersion", LLVersionInfo::instance().getBuild()); LLXMLRPCValue params = LLXMLRPCValue::createArray(); params.append(keywordArgs); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 4cc43254a5..64b7880938 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -765,7 +765,7 @@ LLSD LLFloaterReporter::gatherReport() std::ostringstream details; - details << "V" << LLVersionInfo::getVersion() << std::endl << std::endl; // client version moved to body of email for abuse reports + details << "V" << LLVersionInfo::instance().getVersion() << std::endl << std::endl; // client version moved to body of email for abuse reports std::string object_name = getChild("object_name")->getValue().asString(); if (!object_name.empty() && !mOwnerName.empty()) @@ -783,7 +783,7 @@ LLSD LLFloaterReporter::gatherReport() std::string version_string; version_string = llformat( "%s %s %s %s %s", - LLVersionInfo::getShortVersion().c_str(), + LLVersionInfo::instance().getShortVersion().c_str(), platform, gSysCPU.getFamily().c_str(), gGLManager.mGLRenderer.c_str(), diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 8a69acb8dc..a3c78cc5a5 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -215,8 +215,8 @@ void LLLoginInstance::constructAuthParams(LLPointer user_credentia request_params["last_exec_event"] = mLastExecEvent; request_params["last_exec_duration"] = mLastExecDuration; request_params["mac"] = (char*)hashed_unique_id_string; - request_params["version"] = LLVersionInfo::getVersion(); - request_params["channel"] = LLVersionInfo::getChannel(); + request_params["version"] = LLVersionInfo::instance().getVersion(); + request_params["channel"] = LLVersionInfo::instance().getChannel(); request_params["platform"] = mPlatform; request_params["address_size"] = ADDRESS_SIZE; request_params["platform_version"] = mPlatformVersion; diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 224cec9650..70757882d8 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -338,10 +338,10 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLButton* def_btn = getChild("connect_btn"); setDefaultBtn(def_btn); - std::string channel = LLVersionInfo::getChannel(); + std::string channel = LLVersionInfo::instance().getChannel(); std::string version = llformat("%s (%d)", - LLVersionInfo::getShortVersion().c_str(), - LLVersionInfo::getBuild()); + LLVersionInfo::instance().getShortVersion().c_str(), + LLVersionInfo::instance().getBuild()); LLTextBox* forgot_password_text = getChild("forgot_password_text"); forgot_password_text->setClickedCallback(onClickForgotPassword, NULL); @@ -943,9 +943,9 @@ void LLPanelLogin::loadLoginPage() // Channel and Version params["version"] = llformat("%s (%d)", - LLVersionInfo::getShortVersion().c_str(), - LLVersionInfo::getBuild()); - params["channel"] = LLVersionInfo::getChannel(); + LLVersionInfo::instance().getShortVersion().c_str(), + LLVersionInfo::instance().getBuild()); + params["channel"] = LLVersionInfo::instance().getChannel(); // Grid params["grid"] = LLGridManager::getInstance()->getGridId(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index be6e9e520a..de046440c5 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -512,9 +512,9 @@ bool idle_startup() if(!start_messaging_system( message_template_path, port, - LLVersionInfo::getMajor(), - LLVersionInfo::getMinor(), - LLVersionInfo::getPatch(), + LLVersionInfo::instance().getMajor(), + LLVersionInfo::instance().getMinor(), + LLVersionInfo::instance().getPatch(), FALSE, std::string(), responder, @@ -2309,8 +2309,8 @@ void login_callback(S32 option, void *userdata) void show_release_notes_if_required() { static bool release_notes_shown = false; - if (!release_notes_shown && (LLVersionInfo::getChannelAndVersion() != gLastRunVersion) - && LLVersionInfo::getViewerMaturity() != LLVersionInfo::TEST_VIEWER // don't show Release Notes for the test builds + if (!release_notes_shown && (LLVersionInfo::instance().getChannelAndVersion() != gLastRunVersion) + && LLVersionInfo::instance().getViewerMaturity() != LLVersionInfo::TEST_VIEWER // don't show Release Notes for the test builds && gSavedSettings.getBOOL("UpdaterShowReleaseNotes") && !gSavedSettings.getBOOL("FirstLoginThisInstall")) { diff --git a/indra/newview/lltexturestats.cpp b/indra/newview/lltexturestats.cpp index b55b4d9ca4..8f4b7d000c 100644 --- a/indra/newview/lltexturestats.cpp +++ b/indra/newview/lltexturestats.cpp @@ -46,8 +46,8 @@ void send_texture_stats_to_sim(const LLSD &texture_stats) LLUUID agent_id = gAgent.getID(); texture_stats_report["agent_id"] = agent_id; texture_stats_report["region_id"] = gAgent.getRegion()->getRegionID(); - texture_stats_report["viewer_channel"] = LLVersionInfo::getChannel(); - texture_stats_report["viewer_version"] = LLVersionInfo::getVersion(); + texture_stats_report["viewer_channel"] = LLVersionInfo::instance().getChannel(); + texture_stats_report["viewer_version"] = LLVersionInfo::instance().getVersion(); texture_stats_report["stats_data"] = texture_stats; std::string texture_cap_url = gAgent.getRegion()->getCapability("TextureStats"); diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index e424983cf8..fa3b44f702 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -134,11 +134,11 @@ void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, std:: std::string user_agent = llformat("%s %d.%d.%d (%d)", - LLVersionInfo::getChannel().c_str(), - LLVersionInfo::getMajor(), - LLVersionInfo::getMinor(), - LLVersionInfo::getPatch(), - LLVersionInfo::getBuild()); + LLVersionInfo::instance().getChannel().c_str(), + LLVersionInfo::instance().getMajor(), + LLVersionInfo::instance().getMinor(), + LLVersionInfo::instance().getPatch(), + LLVersionInfo::instance().getBuild()); httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); @@ -177,11 +177,11 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s std::string user_agent = llformat("%s %d.%d.%d (%d)", - LLVersionInfo::getChannel().c_str(), - LLVersionInfo::getMajor(), - LLVersionInfo::getMinor(), - LLVersionInfo::getPatch(), - LLVersionInfo::getBuild()); + LLVersionInfo::instance().getChannel().c_str(), + LLVersionInfo::instance().getMajor(), + LLVersionInfo::instance().getMinor(), + LLVersionInfo::instance().getPatch(), + LLVersionInfo::instance().getBuild()); httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp index 4e07223784..f4b1f2566d 100644 --- a/indra/newview/llversioninfo.cpp +++ b/indra/newview/llversioninfo.cpp @@ -29,6 +29,7 @@ #include #include #include "llversioninfo.h" +#include "stringize.h" #include #if ! defined(LL_VIEWER_CHANNEL) \ @@ -43,100 +44,81 @@ // Set the version numbers in indra/VIEWER_VERSION // -//static +LLVersionInfo::LLVersionInfo(): + // LL_VIEWER_CHANNEL is a macro defined on the compiler command line. The + // macro expands to the string name of the channel, but without quotes. We + // need to turn it into a quoted string. LL_TO_STRING() does that. + mWorkingChannelName(LL_TO_STRING(LL_VIEWER_CHANNEL)), + build_configuration(LLBUILD_CONFIG), // set in indra/cmake/BuildVersion.cmake + short_version(STRINGIZE(LL_VIEWER_VERSION_MAJOR << "." + << LL_VIEWER_VERSION_MINOR << "." + << LL_VIEWER_VERSION_PATCH)) +{ +} + +void LLVersionInfo::initSingleton() +{ + // We override initSingleton() not because we have dependencies on other + // LLSingletons, but because certain initializations call other member + // functions. We should refrain from calling methods until this object is + // fully constructed; such calls don't really belong in the constructor. + + // cache the version string + version = STRINGIZE(getShortVersion() << "." << getBuild()); +} + S32 LLVersionInfo::getMajor() { return LL_VIEWER_VERSION_MAJOR; } -//static S32 LLVersionInfo::getMinor() { return LL_VIEWER_VERSION_MINOR; } -//static S32 LLVersionInfo::getPatch() { return LL_VIEWER_VERSION_PATCH; } -//static S32 LLVersionInfo::getBuild() { return LL_VIEWER_VERSION_BUILD; } -//static -const std::string &LLVersionInfo::getVersion() +std::string LLVersionInfo::getVersion() { - static std::string version(""); - if (version.empty()) - { - std::ostringstream stream; - stream << LLVersionInfo::getShortVersion() << "." << LLVersionInfo::getBuild(); - // cache the version string - version = stream.str(); - } return version; } -//static -const std::string &LLVersionInfo::getShortVersion() +std::string LLVersionInfo::getShortVersion() { - static std::string short_version(""); - if(short_version.empty()) - { - // cache the version string - std::ostringstream stream; - stream << LL_VIEWER_VERSION_MAJOR << "." - << LL_VIEWER_VERSION_MINOR << "." - << LL_VIEWER_VERSION_PATCH; - short_version = stream.str(); - } return short_version; } -namespace -{ - // LL_VIEWER_CHANNEL is a macro defined on the compiler command line. The - // macro expands to the string name of the channel, but without quotes. We - // need to turn it into a quoted string. LL_TO_STRING() does that. - /// Storage of the channel name the viewer is using. - // The channel name is set by hardcoded constant, - // or by calling LLVersionInfo::resetChannel() - std::string sWorkingChannelName(LL_TO_STRING(LL_VIEWER_CHANNEL)); - - // Storage for the "version and channel" string. - // This will get reset too. - std::string sVersionChannel(""); -} - -//static -const std::string &LLVersionInfo::getChannelAndVersion() +std::string LLVersionInfo::getChannelAndVersion() { - if (sVersionChannel.empty()) + if (mVersionChannel.empty()) { // cache the version string - sVersionChannel = LLVersionInfo::getChannel() + " " + LLVersionInfo::getVersion(); + mVersionChannel = getChannel() + " " + getVersion(); } - return sVersionChannel; + return mVersionChannel; } -//static -const std::string &LLVersionInfo::getChannel() +std::string LLVersionInfo::getChannel() { - return sWorkingChannelName; + return mWorkingChannelName; } void LLVersionInfo::resetChannel(const std::string& channel) { - sWorkingChannelName = channel; - sVersionChannel.clear(); // Reset version and channel string til next use. + mWorkingChannelName = channel; + mVersionChannel.clear(); // Reset version and channel string til next use. } -//static LLVersionInfo::ViewerMaturity LLVersionInfo::getViewerMaturity() { ViewerMaturity maturity; @@ -175,8 +157,7 @@ LLVersionInfo::ViewerMaturity LLVersionInfo::getViewerMaturity() } -const std::string &LLVersionInfo::getBuildConfig() +std::string LLVersionInfo::getBuildConfig() { - static const std::string build_configuration(LLBUILD_CONFIG); // set in indra/cmake/BuildVersion.cmake return build_configuration; } diff --git a/indra/newview/llversioninfo.h b/indra/newview/llversioninfo.h index b8b4341385..7857468697 100644 --- a/indra/newview/llversioninfo.h +++ b/indra/newview/llversioninfo.h @@ -30,6 +30,7 @@ #include #include "stdtypes.h" +#include "llsingleton.h" /// /// This API provides version information for the viewer. This @@ -38,42 +39,44 @@ /// viewer code that wants to query the current version should /// use this API. /// -class LLVersionInfo +class LLVersionInfo: public LLSingleton { + LLSINGLETON(LLVersionInfo); + void initSingleton(); public: - /// return the major verion number as an integer - static S32 getMajor(); + /// return the major version number as an integer + S32 getMajor(); - /// return the minor verion number as an integer - static S32 getMinor(); + /// return the minor version number as an integer + S32 getMinor(); - /// return the patch verion number as an integer - static S32 getPatch(); + /// return the patch version number as an integer + S32 getPatch(); /// return the build number as an integer - static S32 getBuild(); + S32 getBuild(); /// return the full viewer version as a string like "2.0.0.200030" - static const std::string &getVersion(); + std::string getVersion(); /// return the viewer version as a string like "2.0.0" - static const std::string &getShortVersion(); + std::string getShortVersion(); /// return the viewer version and channel as a string /// like "Second Life Release 2.0.0.200030" - static const std::string &getChannelAndVersion(); + std::string getChannelAndVersion(); /// return the channel name, e.g. "Second Life" - static const std::string &getChannel(); + std::string getChannel(); /// return the CMake build type - static const std::string &getBuildConfig(); + std::string getBuildConfig(); /// reset the channel name used by the viewer. - static void resetChannel(const std::string& channel); + void resetChannel(const std::string& channel); /// return the bit width of an address - static const S32 getAddressSize() { return ADDRESS_SIZE; } + S32 getAddressSize() { return ADDRESS_SIZE; } typedef enum { @@ -82,7 +85,19 @@ public: BETA_VIEWER, RELEASE_VIEWER } ViewerMaturity; - static ViewerMaturity getViewerMaturity(); + ViewerMaturity getViewerMaturity(); + +private: + std::string version; + std::string short_version; + /// Storage of the channel name the viewer is using. + // The channel name is set by hardcoded constant, + // or by calling resetChannel() + std::string mWorkingChannelName; + // Storage for the "version and channel" string. + // This will get reset too. + std::string mVersionChannel; + std::string build_configuration; }; #endif diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 99b54f66d3..572e53c37f 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -412,7 +412,7 @@ std::string LLViewerMedia::getCurrentUserAgent() // Just in case we need to check browser differences in A/B test // builds. - std::string channel = LLVersionInfo::getChannel(); + std::string channel = LLVersionInfo::instance().getChannel(); // append our magic version number string to the browser user agent id // See the HTTP 1.0 and 1.1 specifications for allowed formats: @@ -422,7 +422,7 @@ std::string LLViewerMedia::getCurrentUserAgent() // http://www.mozilla.org/build/revised-user-agent-strings.html std::ostringstream codec; codec << "SecondLife/"; - codec << LLVersionInfo::getVersion(); + codec << LLVersionInfo::instance().getVersion(); codec << " (" << channel << "; " << skin_name << " skin)"; LL_INFOS() << codec.str() << LL_ENDL; diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 85d87a43af..0f58933005 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -473,7 +473,7 @@ void send_stats() // send fps only for time app spends in foreground agent["fps"] = (F32)gForegroundFrameCount / gForegroundTime.getElapsedTimeF32(); - agent["version"] = LLVersionInfo::getChannelAndVersion(); + agent["version"] = LLVersionInfo::instance().getChannelAndVersion(); std::string language = LLUI::getLanguage(); agent["language"] = language; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index ff5dff1c9b..c045fd8aa6 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -2477,7 +2477,7 @@ void LLViewerWindow::setMenuBackgroundColor(bool god_mode, bool dev_grid) } else { - switch (LLVersionInfo::getViewerMaturity()) + switch (LLVersionInfo::instance().getViewerMaturity()) { case LLVersionInfo::TEST_VIEWER: new_bg_color = LLUIColorTable::instance().getColor( "MenuBarTestBgColor" ); diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 739f7bd47c..358396632c 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -527,7 +527,7 @@ void LLVivoxVoiceClient::connectorCreate() << ".log" << "" << vivoxLogLevel << "" << "" - << "" << LLVersionInfo::getChannel().c_str() << " " << LLVersionInfo::getVersion().c_str() << "" + << "" << LLVersionInfo::instance().getChannel() << " " << LLVersionInfo::instance().getVersion() << "" //<< "" //Name can cause problems per vivox. << "12" << "\n\n\n"; diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp index a34c5826ed..63257d6543 100644 --- a/indra/newview/llweb.cpp +++ b/indra/newview/llweb.cpp @@ -157,12 +157,12 @@ std::string LLWeb::expandURLSubstitutions(const std::string &url, const LLSD &default_subs) { LLSD substitution = default_subs; - substitution["VERSION"] = LLVersionInfo::getVersion(); - substitution["VERSION_MAJOR"] = LLVersionInfo::getMajor(); - substitution["VERSION_MINOR"] = LLVersionInfo::getMinor(); - substitution["VERSION_PATCH"] = LLVersionInfo::getPatch(); - substitution["VERSION_BUILD"] = LLVersionInfo::getBuild(); - substitution["CHANNEL"] = LLVersionInfo::getChannel(); + substitution["VERSION"] = LLVersionInfo::instance().getVersion(); + substitution["VERSION_MAJOR"] = LLVersionInfo::instance().getMajor(); + substitution["VERSION_MINOR"] = LLVersionInfo::instance().getMinor(); + substitution["VERSION_PATCH"] = LLVersionInfo::instance().getPatch(); + substitution["VERSION_BUILD"] = LLVersionInfo::instance().getBuild(); + substitution["CHANNEL"] = LLVersionInfo::instance().getChannel(); substitution["GRID"] = LLGridManager::getInstance()->getGridId(); substitution["GRID_LOWERCASE"] = utf8str_tolower(LLGridManager::getInstance()->getGridId()); substitution["OS"] = LLOSInfo::instance().getOSStringSimple(); diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 2edad30493..57f2d31eab 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -202,8 +202,6 @@ void LLUIColorTable::saveUserSettings(void)const {} //----------------------------------------------------------------------------- #include "../llversioninfo.h" -const std::string &LLVersionInfo::getVersion() { return VIEWERLOGIN_VERSION; } -const std::string &LLVersionInfo::getChannel() { return VIEWERLOGIN_CHANNEL; } bool llHashedUniqueID(unsigned char* id) { diff --git a/indra/newview/tests/llversioninfo_test.cpp b/indra/newview/tests/llversioninfo_test.cpp index 58f0469552..51a6f8f113 100644 --- a/indra/newview/tests/llversioninfo_test.cpp +++ b/indra/newview/tests/llversioninfo_test.cpp @@ -83,39 +83,39 @@ namespace tut void versioninfo_object_t::test<1>() { std::cout << "What we parsed from CMake: " << LL_VIEWER_VERSION_BUILD << std::endl; - std::cout << "What we get from llversioninfo: " << LLVersionInfo::getBuild() << std::endl; + std::cout << "What we get from llversioninfo: " << LLVersionInfo::instance().getBuild() << std::endl; ensure_equals("Major version", - LLVersionInfo::getMajor(), + LLVersionInfo::instance().getMajor(), LL_VIEWER_VERSION_MAJOR); ensure_equals("Minor version", - LLVersionInfo::getMinor(), + LLVersionInfo::instance().getMinor(), LL_VIEWER_VERSION_MINOR); ensure_equals("Patch version", - LLVersionInfo::getPatch(), + LLVersionInfo::instance().getPatch(), LL_VIEWER_VERSION_PATCH); ensure_equals("Build version", - LLVersionInfo::getBuild(), + LLVersionInfo::instance().getBuild(), LL_VIEWER_VERSION_BUILD); ensure_equals("Channel version", - LLVersionInfo::getChannel(), + LLVersionInfo::instance().getChannel(), ll_viewer_channel); ensure_equals("Version String", - LLVersionInfo::getVersion(), + LLVersionInfo::instance().getVersion(), mVersion); ensure_equals("Short Version String", - LLVersionInfo::getShortVersion(), + LLVersionInfo::instance().getShortVersion(), mShortVersion); ensure_equals("Version and channel String", - LLVersionInfo::getChannelAndVersion(), + LLVersionInfo::instance().getChannelAndVersion(), mVersionAndChannel); - LLVersionInfo::resetChannel(mResetChannel); + LLVersionInfo::instance().resetChannel(mResetChannel); ensure_equals("Reset channel version", - LLVersionInfo::getChannel(), + LLVersionInfo::instance().getChannel(), mResetChannel); ensure_equals("Reset Version and channel String", - LLVersionInfo::getChannelAndVersion(), + LLVersionInfo::instance().getChannelAndVersion(), mResetVersionAndChannel); } } -- cgit v1.3 From 378e4fae94c9c16b0b9070d7c7d3f63cba8aee94 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 30 May 2019 16:35:45 -0400 Subject: SL-11216: Introduce LLVersionInfo::getReleaseNotes() method. The default string returned by getReleaseNotes() is empty. It must be set by posting the relevant release-notes URL string to a new LLEventMailDrop instance named "relnotes". Add unique_ptr and unique_ptr> to LLVersionInfo -- using unique_ptr to leave those classes opaque to header-file consumers. Introduce an out-of-line destructor to handle the unique_ptr idiom. Initialize the LLEventMailDrop with the desired name; initialize the LLStoreListener with that LLEventMailDrop and the data member returned by getReleaseNotes(). --- indra/newview/llversioninfo.cpp | 24 +++++++++++++++++++----- indra/newview/llversioninfo.h | 21 ++++++++++++++++++++- 2 files changed, 39 insertions(+), 6 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp index f4b1f2566d..4720a989b0 100644 --- a/indra/newview/llversioninfo.cpp +++ b/indra/newview/llversioninfo.cpp @@ -26,8 +26,8 @@ */ #include "llviewerprecompiledheaders.h" -#include -#include +#include "llevents.h" +#include "lleventfilter.h" #include "llversioninfo.h" #include "stringize.h" #include @@ -45,14 +45,19 @@ // LLVersionInfo::LLVersionInfo(): + short_version(STRINGIZE(LL_VIEWER_VERSION_MAJOR << "." + << LL_VIEWER_VERSION_MINOR << "." + << LL_VIEWER_VERSION_PATCH)), // LL_VIEWER_CHANNEL is a macro defined on the compiler command line. The // macro expands to the string name of the channel, but without quotes. We // need to turn it into a quoted string. LL_TO_STRING() does that. mWorkingChannelName(LL_TO_STRING(LL_VIEWER_CHANNEL)), build_configuration(LLBUILD_CONFIG), // set in indra/cmake/BuildVersion.cmake - short_version(STRINGIZE(LL_VIEWER_VERSION_MAJOR << "." - << LL_VIEWER_VERSION_MINOR << "." - << LL_VIEWER_VERSION_PATCH)) + // instantiate an LLEventMailDrop with canonical name to listen for news + // from SLVersionChecker + mPump{new LLEventMailDrop("relnotes")}, + // immediately listen on mPump, store arriving URL into mReleaseNotes + mStore{new LLStoreListener(*mPump, mReleaseNotes)} { } @@ -67,6 +72,10 @@ void LLVersionInfo::initSingleton() version = STRINGIZE(getShortVersion() << "." << getBuild()); } +LLVersionInfo::~LLVersionInfo() +{ +} + S32 LLVersionInfo::getMajor() { return LL_VIEWER_VERSION_MAJOR; @@ -161,3 +170,8 @@ std::string LLVersionInfo::getBuildConfig() { return build_configuration; } + +std::string LLVersionInfo::getReleaseNotes() +{ + return mReleaseNotes; +} diff --git a/indra/newview/llversioninfo.h b/indra/newview/llversioninfo.h index 7857468697..02ff0c094a 100644 --- a/indra/newview/llversioninfo.h +++ b/indra/newview/llversioninfo.h @@ -28,9 +28,14 @@ #ifndef LL_LLVERSIONINFO_H #define LL_LLVERSIONINFO_H -#include #include "stdtypes.h" #include "llsingleton.h" +#include +#include + +class LLEventMailDrop; +template +class LLStoreListener; /// /// This API provides version information for the viewer. This @@ -44,6 +49,8 @@ class LLVersionInfo: public LLSingleton LLSINGLETON(LLVersionInfo); void initSingleton(); public: + ~LLVersionInfo(); + /// return the major version number as an integer S32 getMajor(); @@ -87,6 +94,10 @@ public: } ViewerMaturity; ViewerMaturity getViewerMaturity(); + /// get the release-notes URL, once it becomes available -- until then, + /// return empty string + std::string getReleaseNotes(); + private: std::string version; std::string short_version; @@ -98,6 +109,14 @@ private: // This will get reset too. std::string mVersionChannel; std::string build_configuration; + std::string mReleaseNotes; + // Store unique_ptrs to the next couple things so we don't have to explain + // to every consumer of this header file all the details of each. + // mPump is the LLEventMailDrop on which we listen for SLVersionChecker to + // post the release-notes URL from the Viewer Version Manager. + std::unique_ptr mPump; + // mStore is an adapter that stores the release-notes URL in mReleaseNotes. + std::unique_ptr> mStore; }; #endif -- cgit v1.3 From 7fbbd4b766a86c3b8d0d0ed3b9e2ebf3fd27a889 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 31 May 2019 16:42:07 -0400 Subject: SL-11216: Add a "getStateTable" op to "LLStartUp" listener. getStateTable returns a list of the EStartupState symbolic names, implicitly mapping each to its index (its enum numeric value). --- indra/newview/llstartup.h | 1 + indra/newview/llstartuplistener.cpp | 26 +++++++++++++++++++++++++- indra/newview/llstartuplistener.h | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index d7d294e9f4..3ec3ff4133 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -128,6 +128,7 @@ public: static LLViewerStats::PhaseMap& getPhases() { return *sPhases; } private: + friend class LLStartupListener; static LLSLURL sStartSLURL; static std::string startupStateToString(EStartupState state); diff --git a/indra/newview/llstartuplistener.cpp b/indra/newview/llstartuplistener.cpp index d9a21f908e..5770b595d0 100644 --- a/indra/newview/llstartuplistener.cpp +++ b/indra/newview/llstartuplistener.cpp @@ -35,7 +35,7 @@ // external library headers // other Linden headers #include "llstartup.h" - +#include "stringize.h" LLStartupListener::LLStartupListener(/* LLStartUp* instance */): LLEventAPI("LLStartUp", "Access e.g. LLStartup::postStartupState()") /* , @@ -43,9 +43,33 @@ LLStartupListener::LLStartupListener(/* LLStartUp* instance */): { add("postStartupState", "Refresh \"StartupState\" listeners with current startup state", &LLStartupListener::postStartupState); + add("getStateTable", "Reply with array of EStartupState string names", + &LLStartupListener::getStateTable); } void LLStartupListener::postStartupState(const LLSD&) const { LLStartUp::postStartupState(); } + +void LLStartupListener::getStateTable(const LLSD& event) const +{ + Response response(LLSD(), event); + + // This relies on our knowledge that STATE_STARTED is the very last + // EStartupState value. If that ever stops being true, we're going to lie + // without realizing it. I can think of no reliable way to test whether + // the enum has been extended *beyond* STATE_STARTED. We could, of course, + // test whether stuff has been inserted before it, by testing its + // numerical value against the constant value as of the last time we + // looked; but that's pointless, as values inserted before STATE_STARTED + // will continue to work fine. The bad case is if new symbols get added + // *after* it. + LLSD table; + // note <= comparison: we want to *include* STATE_STARTED. + for (LLSD::Integer istate{0}; istate <= LLSD::Integer(STATE_STARTED); ++istate) + { + table.append(LLStartUp::startupStateToString(EStartupState(istate))); + } + response["table"] = table; +} diff --git a/indra/newview/llstartuplistener.h b/indra/newview/llstartuplistener.h index a35e11f6eb..0b4380a568 100644 --- a/indra/newview/llstartuplistener.h +++ b/indra/newview/llstartuplistener.h @@ -40,6 +40,7 @@ public: private: void postStartupState(const LLSD&) const; + void getStateTable(const LLSD&) const; //LLStartup* mStartup; }; -- cgit v1.3 From 15418f991a77416e765e985c39267bb6d4c51a6f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 31 May 2019 16:44:58 -0400 Subject: SL-11216: getViewerInfo() calls LLVersionInfo::getReleaseNotes(). Make LLAppViewer retrieve release notes from LLVersionInfo, rather than synthesizing the release-notes URL itself based on the viewer version string. --- indra/newview/llappviewer.cpp | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 795d14740a..ef4500c01f 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3061,16 +3061,12 @@ LLSD LLAppViewer::getViewerInfo() const // is available to a getInfo() caller as to the user opening // LLFloaterAbout. LLSD info; - LLSD version; - version.append(LLVersionInfo::instance().getMajor()); - version.append(LLVersionInfo::instance().getMinor()); - version.append(LLVersionInfo::instance().getPatch()); - version.append(LLVersionInfo::instance().getBuild()); - info["VIEWER_VERSION"] = version; - info["VIEWER_VERSION_STR"] = LLVersionInfo::instance().getVersion(); - info["CHANNEL"] = LLVersionInfo::instance().getChannel(); + auto& versionInfo{ LLVersionInfo::instance() }; + info["VIEWER_VERSION"] = LLSDArray(versionInfo.getMajor())(versionInfo.getMinor())(versionInfo.getPatch())(versionInfo.getBuild()); + info["VIEWER_VERSION_STR"] = versionInfo.getVersion(); + info["CHANNEL"] = versionInfo.getChannel(); info["ADDRESS_SIZE"] = ADDRESS_SIZE; - std::string build_config = LLVersionInfo::instance().getBuildConfig(); + std::string build_config = versionInfo.getBuildConfig(); if (build_config != "Release") { info["BUILD_CONFIG"] = build_config; @@ -3078,12 +3074,8 @@ LLSD LLAppViewer::getViewerInfo() const // return a URL to the release notes for this viewer, such as: // https://releasenotes.secondlife.com/viewer/2.1.0.123456.html - std::string url = LLTrans::getString("RELEASE_NOTES_BASE_URL"); - if (! LLStringUtil::endsWith(url, "/")) - url += "/"; - url += LLURI::escape(LLVersionInfo::instance().getVersion()) + ".html"; - - info["VIEWER_RELEASE_NOTES_URL"] = url; + std::string url = versionInfo.getReleaseNotes(); + info["VIEWER_RELEASE_NOTES_URL"] = url.empty()? LLTrans::getString("RetrievingData") : url; // Position LLViewerRegion* region = gAgent.getRegion(); -- cgit v1.3 From 31d9930a0ff7da5a6312a8f47037052cd2d06bdb Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 31 May 2019 16:47:20 -0400 Subject: SL-11216: To display release notes, listen on "relnotes" LLEventPump. Now, when the viewer decides it's appropriate to display release notes on the login screen, wait for SLVersionChecker to post the release-notes URL before opening the web floater. --- indra/newview/llstartup.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index de046440c5..7c19dfac75 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2309,13 +2309,29 @@ void login_callback(S32 option, void *userdata) void show_release_notes_if_required() { static bool release_notes_shown = false; + // We happen to know that instantiating LLVersionInfo implicitly + // instantiates the LLEventMailDrop named "relnotes", which we (might) use + // below. If viewer release notes stop working, might be because that + // LLEventMailDrop got moved out of LLVersionInfo and hasn't yet been + // instantiated. if (!release_notes_shown && (LLVersionInfo::instance().getChannelAndVersion() != gLastRunVersion) && LLVersionInfo::instance().getViewerMaturity() != LLVersionInfo::TEST_VIEWER // don't show Release Notes for the test builds && gSavedSettings.getBOOL("UpdaterShowReleaseNotes") && !gSavedSettings.getBOOL("FirstLoginThisInstall")) { - LLSD info(LLAppViewer::instance()->getViewerInfo()); - LLWeb::loadURLInternal(info["VIEWER_RELEASE_NOTES_URL"]); + // Instantiate a "relnotes" listener which assumes any arriving event + // is the release notes URL string. Since "relnotes" is an + // LLEventMailDrop, this listener will be invoked whether or not the + // URL has already been posted. If so, it will fire immediately; + // otherwise it will fire whenever the URL is (later) posted. Either + // way, it will display the release notes as soon as the URL becomes + // available. + LLEventPumps::instance().obtain("relnotes").listen( + "showrelnotes", + [](const LLSD& url){ + LLWeb::loadURLInternal(url.asString()); + return false; + }); release_notes_shown = true; } } -- cgit v1.3 From 8961be780d5c5ffa4f9cfc8060756a1bac5fc69f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sat, 1 Jun 2019 09:30:36 -0400 Subject: SL-11216: Try to pacify VS 2013. --- indra/newview/llappviewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ef4500c01f..af70751b37 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3061,7 +3061,7 @@ LLSD LLAppViewer::getViewerInfo() const // is available to a getInfo() caller as to the user opening // LLFloaterAbout. LLSD info; - auto& versionInfo{ LLVersionInfo::instance() }; + auto& versionInfo(LLVersionInfo::instance()); info["VIEWER_VERSION"] = LLSDArray(versionInfo.getMajor())(versionInfo.getMinor())(versionInfo.getPatch())(versionInfo.getBuild()); info["VIEWER_VERSION_STR"] = versionInfo.getVersion(); info["CHANNEL"] = versionInfo.getChannel(); -- cgit v1.3 From bf999f2f84dd26844c60d682f563f982a55e8ee8 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 23 May 2019 15:27:37 -0400 Subject: SL-11215: Add release notes URLs to update-related notifications. Add code to login-fail handler to provide release notes URL from SLVersionChecker handshake event. --- indra/newview/lllogininstance.cpp | 26 ++++++++++++++++++---- .../newview/skins/default/xui/en/notifications.xml | 5 +++++ 2 files changed, 27 insertions(+), 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index a3c78cc5a5..3f665971cb 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -332,7 +332,7 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event) { data["certificate"] = response["certificate"]; } - + if (gViewerWindow) gViewerWindow->setShowProgress(FALSE); @@ -349,13 +349,31 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event) // login.cgi is insisting on a required update. We were called with an // event that bundles both the login.cgi 'response' and the // synchronization event from the 'updater'. - std::string required_version = response["message_args"]["VERSION"]; - LL_WARNS("LLLogin") << "Login failed because an update to version " << required_version << " is required." << LL_ENDL; + std::string login_version = response["message_args"]["VERSION"]; + std::string vvm_version = updater["VERSION"]; + std::string relnotes = updater["URL"]; + LL_WARNS("LLLogin") << "Login failed because an update to version " << login_version << " is required." << LL_ENDL; + // vvm_version might be empty because we might not have gotten + // SLVersionChecker's LoginSync handshake. But if it IS populated, it + // should (!) be the same as the version we got from login.cgi. + if ((! vvm_version.empty()) && vvm_version != login_version) + { + LL_WARNS("LLLogin") << "VVM update version " << vvm_version + << " differs from login version " << login_version + << "; presenting VVM version to match release notes URL" + << LL_ENDL; + login_version = vvm_version; + } + if (relnotes.empty()) + { + // I thought this would be available in strings.xml or some such + relnotes = "https://secondlife.com/support/downloads/"; + } if (gViewerWindow) gViewerWindow->setShowProgress(FALSE); - LLSD args(LLSDMap("VERSION", required_version)); + LLSD args(LLSDMap("VERSION", login_version)("URL", relnotes)); if (updater.isUndefined()) { // If the updater failed to shake hands, better advise the user to diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 57183ac3a6..04235e5d81 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -4016,6 +4016,8 @@ Finished download of raw terrain file to: [DOWNLOAD_PATH]. + Version [VERSION] is required for login. +Release notes: [URL] Click OK to download and install. confirm Version [VERSION] has been downloaded and is ready to install. +Release notes: [URL] Click OK to install. confirm Version [VERSION] has been downloaded and is ready to install. +Release notes: [URL] Proceed? confirm Date: Thu, 25 Oct 2018 11:24:06 -0400 Subject: DRTVWR-476: Fix _open_osfhandle() param from long to intptr_t. The Microsoft _open_osfhandle() opens a HANDLE to produce a C-style int file descriptor suitable for passing to _fdopen(). We used to cast the HANDLEs returned by GetStdHandle() to long to pass to _open_osfhandle(). Since HANDLE is an alias for a pointer, this no longer works. Fortunately _open_osfhandle() now accepts intptr_t, so we can change the relevant GetStdHandle() calls. (But why not simply accept HANDLE in the first place?) --- indra/newview/llappviewerwin32.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index d208e135bb..9a8a5f16bb 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -504,7 +504,7 @@ const S32 MAX_CONSOLE_LINES = 500; static bool create_console() { int h_con_handle; - long l_std_handle; + intptr_t l_std_handle; CONSOLE_SCREEN_BUFFER_INFO coninfo; FILE *fp; @@ -518,7 +518,7 @@ static bool create_console() SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); // redirect unbuffered STDOUT to the console - l_std_handle = (long)GetStdHandle(STD_OUTPUT_HANDLE); + l_std_handle = reinterpret_cast(GetStdHandle(STD_OUTPUT_HANDLE)); h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); if (h_con_handle == -1) { @@ -532,7 +532,7 @@ static bool create_console() } // redirect unbuffered STDIN to the console - l_std_handle = (long)GetStdHandle(STD_INPUT_HANDLE); + l_std_handle = reinterpret_cast(GetStdHandle(STD_INPUT_HANDLE)); h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); if (h_con_handle == -1) { @@ -546,7 +546,7 @@ static bool create_console() } // redirect unbuffered STDERR to the console - l_std_handle = (long)GetStdHandle(STD_ERROR_HANDLE); + l_std_handle = reinterpret_cast(GetStdHandle(STD_ERROR_HANDLE)); h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); if (h_con_handle == -1) { -- cgit v1.3 From e9c667af96278d277eb2a20a6d8364d21464c3eb Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 25 Oct 2018 11:26:39 -0400 Subject: DRTVWR-476: Eliminate std::mem_fun1() special case for Windows. We used to have to use #if LL_WINDOWS logic to pass std::mem_fun1() to llbind2nd() instead of std::mem_fun() elsewhere. VS 2017 no longer supports std::mem_fun1(), which means we can eliminate the special case for Windows. --- indra/newview/llfloaterregioninfo.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index ec934a4732..8a4f5dfc49 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -573,11 +573,7 @@ void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region) mInfoPanels.begin(), mInfoPanels.end(), llbind2nd( -#if LL_WINDOWS - std::mem_fun1(&LLPanelRegionInfo::refreshFromRegion), -#else std::mem_fun(&LLPanelRegionInfo::refreshFromRegion), -#endif region)); } -- cgit v1.3 From 66981fab0b3c8dcc3a031d50710a2b24ec6b0603 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 10 May 2018 21:46:07 -0400 Subject: SL-793: Use Boost.Fiber instead of the "dcoroutine" library. Longtime fans will remember that the "dcoroutine" library is a Google Summer of Code project by Giovanni P. Deretta. He originally called it "Boost.Coroutine," and we originally added it to our 3p-boost autobuild package as such. But when the official Boost.Coroutine library came along (with a very different API), and we still needed the API of the GSoC project, we renamed the unofficial one "dcoroutine" to allow coexistence. The "dcoroutine" library had an internal low-level API more or less analogous to Boost.Context. We later introduced an implementation of that internal API based on Boost.Context, a step towards eliminating the GSoC code in favor of official, supported Boost code. However, recent versions of Boost.Context no longer support the API on which we built the shim for "dcoroutine." We started down the path of reimplementing that shim using the current Boost.Context API -- then realized that it's time to bite the bullet and replace the "dcoroutine" API with the Boost.Fiber API, which we've been itching to do for literally years now. Naturally, most of the heavy lifting is in llcoros.{h,cpp} and lleventcoro.{h,cpp} -- which is good: the LLCoros layer abstracts away most of the differences between "dcoroutine" and Boost.Fiber. The one feature Boost.Fiber does not provide is the ability to forcibly terminate some other fiber. Accordingly, disable LLCoros::kill() and LLCoprocedureManager::shutdown(). The only known shutdown() call was in LLCoprocedurePool's destructor. We also took the opportunity to remove postAndSuspend2() and its associated machinery: FutureListener2, LLErrorEvent, errorException(), errorLog(), LLCoroEventPumps. All that dual-LLEventPump stuff was introduced at a time when the Responder pattern was king, and we assumed we'd want to listen on one LLEventPump with the success handler and on another with the error handler. We have never actually used that in practice. Remove associated tests, of course. There is one other semantic difference that necessitates patching a number of tests: with "dcoroutine," fulfilling a future IMMEDIATELY resumes the waiting coroutine. With Boost.Fiber, fulfilling a future merely marks the fiber as ready to resume next time the scheduler gets around to it. To observe the test side effects, we've inserted a number of llcoro::suspend() calls -- also in the main loop. For a long time we retained a single unit test exercising the raw "dcoroutine" API. Remove that. Eliminate llcoro_get_id.{h,cpp}, which provided llcoro::get_id(), which was a hack to emulate fiber-local variables. Since Boost.Fiber has an actual API for that, remove the hack. In fact, use (new alias) LLCoros::local_ptr for LLSingleton's dependency tracking in place of llcoro::get_id(). In CMake land, replace BOOST_COROUTINE_LIBRARY with BOOST_FIBER_LIBRARY. We don't actually use the Boost.Coroutine for anything (though there exist plausible use cases). --- indra/cmake/Boost.cmake | 20 +- indra/cmake/LLAddBuildTest.cmake | 4 +- indra/cmake/LLAppearance.cmake | 2 +- indra/cmake/LLCommon.cmake | 4 +- indra/cmake/LLCoreHttp.cmake | 2 +- indra/linux_crash_logger/CMakeLists.txt | 2 +- indra/llcommon/CMakeLists.txt | 6 +- indra/llcommon/llcoro_get_id.cpp | 32 -- indra/llcommon/llcoro_get_id.h | 30 -- indra/llcommon/llcoros.cpp | 297 +++------- indra/llcommon/llcoros.h | 184 ++----- indra/llcommon/lleventcoro.cpp | 268 +++------ indra/llcommon/lleventcoro.h | 204 +------ indra/llcommon/llsingleton.cpp | 41 +- indra/llcommon/tests/lleventcoro_test.cpp | 598 ++------------------- indra/llmessage/CMakeLists.txt | 8 +- indra/llmessage/llcoproceduremanager.cpp | 6 + indra/llmessage/llcoproceduremanager.h | 2 + indra/llprimitive/CMakeLists.txt | 2 +- indra/llui/CMakeLists.txt | 2 +- indra/mac_crash_logger/CMakeLists.txt | 2 +- indra/newview/CMakeLists.txt | 4 +- indra/newview/llappviewer.cpp | 2 + indra/test/CMakeLists.txt | 2 +- indra/viewer_components/login/CMakeLists.txt | 4 +- .../viewer_components/login/tests/lllogin_test.cpp | 5 + indra/win_crash_logger/CMakeLists.txt | 2 +- 27 files changed, 331 insertions(+), 1404 deletions(-) delete mode 100644 indra/llcommon/llcoro_get_id.cpp delete mode 100644 indra/llcommon/llcoro_get_id.h (limited to 'indra/newview') diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index 180a84dbcf..e05e3ca0e5 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -8,7 +8,7 @@ if (USESYSTEMLIBS) include(FindBoost) set(BOOST_CONTEXT_LIBRARY boost_context-mt) - set(BOOST_COROUTINE_LIBRARY boost_coroutine-mt) + set(BOOST_FIBER_LIBRARY boost_fiber-mt) set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt) set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt) set(BOOST_REGEX_LIBRARY boost_regex-mt) @@ -49,9 +49,9 @@ else (USESYSTEMLIBS) set(BOOST_CONTEXT_LIBRARY optimized libboost_context-mt debug libboost_context-mt-gd) - set(BOOST_COROUTINE_LIBRARY - optimized libboost_coroutine-mt - debug libboost_coroutine-mt-gd) + set(BOOST_FIBER_LIBRARY + optimized libboost_fiber-mt + debug libboost_fiber-mt-gd) set(BOOST_FILESYSTEM_LIBRARY optimized libboost_filesystem-mt debug libboost_filesystem-mt-gd) @@ -75,9 +75,9 @@ else (USESYSTEMLIBS) set(BOOST_CONTEXT_LIBRARY optimized boost_context-mt debug boost_context-mt-d) - set(BOOST_COROUTINE_LIBRARY - optimized boost_coroutine-mt - debug boost_coroutine-mt-d) + set(BOOST_FIBER_LIBRARY + optimized boost_fiber-mt + debug boost_fiber-mt-d) set(BOOST_FILESYSTEM_LIBRARY optimized boost_filesystem-mt debug boost_filesystem-mt-d) @@ -100,9 +100,9 @@ else (USESYSTEMLIBS) set(BOOST_CONTEXT_LIBRARY optimized boost_context-mt debug boost_context-mt-d) - set(BOOST_COROUTINE_LIBRARY - optimized boost_coroutine-mt - debug boost_coroutine-mt-d) + set(BOOST_FIBER_LIBRARY + optimized boost_fiber-mt + debug boost_fiber-mt-d) set(BOOST_FILESYSTEM_LIBRARY optimized boost_filesystem-mt debug boost_filesystem-mt-d) diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index b3f42c1a5e..ee6396e473 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -53,7 +53,7 @@ INCLUDE(GoogleMock) ${GOOGLEMOCK_INCLUDE_DIRS} ) SET(alltest_LIBRARIES - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ${GOOGLEMOCK_LIBRARIES} @@ -201,7 +201,7 @@ FUNCTION(LL_ADD_INTEGRATION_TEST SET(libraries ${library_dependencies} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ${GOOGLEMOCK_LIBRARIES} diff --git a/indra/cmake/LLAppearance.cmake b/indra/cmake/LLAppearance.cmake index ae265d07e3..675330ec72 100644 --- a/indra/cmake/LLAppearance.cmake +++ b/indra/cmake/LLAppearance.cmake @@ -18,7 +18,7 @@ endif (BUILD_HEADLESS) set(LLAPPEARANCE_LIBRARIES llappearance llmessage llcorehttp - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ) diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index 3e29297c58..8900419f9b 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -19,7 +19,7 @@ if (LINUX) # specify all libraries that llcommon uses. # llcommon uses `clock_gettime' which is provided by librt on linux. set(LLCOMMON_LIBRARIES llcommon - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_THREAD_LIBRARY} ${BOOST_SYSTEM_LIBRARY} @@ -27,7 +27,7 @@ if (LINUX) ) else (LINUX) set(LLCOMMON_LIBRARIES llcommon - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_THREAD_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ) diff --git a/indra/cmake/LLCoreHttp.cmake b/indra/cmake/LLCoreHttp.cmake index 379ae207de..613453ab5d 100644 --- a/indra/cmake/LLCoreHttp.cmake +++ b/indra/cmake/LLCoreHttp.cmake @@ -12,6 +12,6 @@ set(LLCOREHTTP_INCLUDE_DIRS ) set(LLCOREHTTP_LIBRARIES llcorehttp - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY}) diff --git a/indra/linux_crash_logger/CMakeLists.txt b/indra/linux_crash_logger/CMakeLists.txt index 315aed8d11..d789c850a0 100644 --- a/indra/linux_crash_logger/CMakeLists.txt +++ b/indra/linux_crash_logger/CMakeLists.txt @@ -69,7 +69,7 @@ target_link_libraries(linux-crash-logger ${LLMATH_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${UI_LIBRARIES} ${DB_LIBRARIES} diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 55c44446b4..2f263cd830 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -44,7 +44,6 @@ set(llcommon_SOURCE_FILES llcleanup.cpp llcommon.cpp llcommonutils.cpp - llcoro_get_id.cpp llcoros.cpp llcrc.cpp llcriticaldamp.cpp @@ -146,7 +145,6 @@ set(llcommon_HEADER_FILES llcleanup.h llcommon.h llcommonutils.h - llcoro_get_id.h llcoros.h llcrc.h llcriticaldamp.h @@ -293,7 +291,7 @@ target_link_libraries( ${JSONCPP_LIBRARIES} ${ZLIB_LIBRARIES} ${WINDOWS_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_PROGRAM_OPTIONS_LIBRARY} ${BOOST_REGEX_LIBRARY} @@ -322,7 +320,7 @@ if (LL_TESTS) ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_THREAD_LIBRARY} ${BOOST_SYSTEM_LIBRARY}) diff --git a/indra/llcommon/llcoro_get_id.cpp b/indra/llcommon/llcoro_get_id.cpp deleted file mode 100644 index 24ed1fe0c9..0000000000 --- a/indra/llcommon/llcoro_get_id.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file llcoro_get_id.cpp - * @author Nat Goodspeed - * @date 2016-09-03 - * @brief Implementation for llcoro_get_id. - * - * $LicenseInfo:firstyear=2016&license=viewerlgpl$ - * Copyright (c) 2016, Linden Research, Inc. - * $/LicenseInfo$ - */ - -// Precompiled header -#include "linden_common.h" -// associated header -#include "llcoro_get_id.h" -// STL headers -// std headers -// external library headers -// other Linden headers -#include "llcoros.h" - -namespace llcoro -{ - -id get_id() -{ - // An instance of Current can convert to LLCoros::CoroData*, which can - // implicitly convert to void*, which is an llcoro::id. - return LLCoros::Current(); -} - -} // llcoro diff --git a/indra/llcommon/llcoro_get_id.h b/indra/llcommon/llcoro_get_id.h deleted file mode 100644 index 4c1dca6f19..0000000000 --- a/indra/llcommon/llcoro_get_id.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @file llcoro_get_id.h - * @author Nat Goodspeed - * @date 2016-09-03 - * @brief Supplement the functionality in llcoro.h. - * - * This is broken out as a separate header file to resolve - * circularity: LLCoros isa LLSingleton, yet LLSingleton machinery - * requires llcoro::get_id(). - * - * Be very suspicious of anyone else #including this header. - * - * $LicenseInfo:firstyear=2016&license=viewerlgpl$ - * Copyright (c) 2016, Linden Research, Inc. - * $/LicenseInfo$ - */ - -#if ! defined(LL_LLCORO_GET_ID_H) -#define LL_LLCORO_GET_ID_H - -namespace llcoro -{ - -/// Get an opaque, distinct token for the running coroutine (or main). -typedef void* id; -id get_id(); - -} // llcoro - -#endif /* ! defined(LL_LLCORO_GET_ID_H) */ diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index cc775775bf..f5ffd96cec 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -34,6 +34,17 @@ // std headers // external library headers #include +#include +#ifndef BOOST_DISABLE_ASSERTS +#define UNDO_BOOST_DISABLE_ASSERTS +// with Boost 1.65.1, needed for Mac with this specific header +#define BOOST_DISABLE_ASSERTS +#endif +#include +#ifdef UNDO_BOOST_DISABLE_ASSERTS +#undef UNDO_BOOST_DISABLE_ASSERTS +#undef BOOST_DISABLE_ASSERTS +#endif // other Linden headers #include "lltimer.h" #include "llevents.h" @@ -45,176 +56,69 @@ #include #endif -namespace { -void no_op() {} -} // anonymous namespace -// Do nothing, when we need nothing done. This is a static member of LLCoros -// because CoroData is a private nested class. -void LLCoros::no_cleanup(CoroData*) {} - -// CoroData for the currently-running coroutine. Use a thread_specific_ptr -// because each thread potentially has its own distinct pool of coroutines. -LLCoros::Current::Current() +const LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller) const { - // Use a function-static instance so this thread_specific_ptr is - // instantiated on demand. Since we happen to know it's consumed by - // LLSingleton, this is likely to happen before the runtime has finished - // initializing module-static data. For the same reason, we can't package - // this pointer in an LLSingleton. - - // This thread_specific_ptr does NOT own the CoroData object! That's owned - // by LLCoros::mCoros. It merely identifies it. For this reason we - // instantiate it with a no-op cleanup function. - static boost::thread_specific_ptr sCurrent(LLCoros::no_cleanup); - - // If this is the first time we're accessing sCurrent for the running - // thread, its get() will be NULL. This could be a problem, in that - // llcoro::get_id() would return the same (NULL) token value for the "main - // coroutine" in every thread, whereas what we really want is a distinct - // value for every distinct stack in the process. So if get() is NULL, - // give it a heap CoroData: this ensures that llcoro::get_id() will return - // distinct values. - // This tactic is "leaky": sCurrent explicitly does not destroy any - // CoroData to which it points, and we do NOT enter these "main coroutine" - // CoroData instances in the LLCoros::mCoros map. They are dummy entries, - // and they will leak at process shutdown: one CoroData per thread. - if (! sCurrent.get()) + CoroData* current = mCurrent.get(); + // For the main() coroutine, the one NOT explicitly launched by launch(), + // we never explicitly set mCurrent. Use a static CoroData instance with + // canonical values. + if (! current) { // It's tempting to provide a distinct name for each thread's "main // coroutine." But as getName() has always returned the empty string - // to mean "not in a coroutine," empty string should suffice here -- - // and truthfully the additional (thread-safe!) machinery to ensure - // uniqueness just doesn't feel worth the trouble. - // We use a no-op callable and a minimal stack size because, although - // CoroData's constructor in fact initializes its mCoro with a - // coroutine with that stack size, no one ever actually enters it by - // calling mCoro(). - sCurrent.reset(new CoroData(0, // no prev - "", // not a named coroutine - no_op, // no-op callable - 1024)); // stacksize moot + // to mean "not in a coroutine," empty string should suffice here. + static CoroData sMain(""); + // We need not reset() the local_ptr to this read-only data: reuse the + // same instance for every thread's main coroutine. + current = &sMain; } - - mCurrent = &sCurrent; + return *current; } -//static LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller) { - CoroData* current = Current(); - // With the dummy CoroData set in LLCoros::Current::Current(), this - // pointer should never be NULL. - llassert_always(current); - return *current; + // reuse const implementation, just cast away const-ness of result + return const_cast(const_cast(this)->get_CoroData(caller)); } //static -LLCoros::coro::self& LLCoros::get_self() +LLCoros::coro::id LLCoros::get_self() { - CoroData& current = get_CoroData("get_self()"); - if (! current.mSelf) - { - LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL; - } - return *current.mSelf; + return boost::this_fiber::get_id(); } //static void LLCoros::set_consuming(bool consuming) { - get_CoroData("set_consuming()").mConsuming = consuming; + CoroData& data(LLCoros::instance().get_CoroData("set_consuming()")); + // DO NOT call this on the main() coroutine. + llassert_always(! data.mName.empty()); + data.mConsuming = consuming; } //static bool LLCoros::get_consuming() { - return get_CoroData("get_consuming()").mConsuming; -} - -llcoro::Suspending::Suspending() -{ - LLCoros::Current current; - // Remember currently-running coroutine: we're about to suspend it. - mSuspended = current; - // Revert Current to the value it had at the moment we last switched - // into this coroutine. - current.reset(mSuspended->mPrev); -} - -llcoro::Suspending::~Suspending() -{ - LLCoros::Current current; - // Okay, we're back, update our mPrev - mSuspended->mPrev = current; - // and reinstate our Current. - current.reset(mSuspended); + return LLCoros::instance().get_CoroData("get_consuming()").mConsuming; } LLCoros::LLCoros(): // MAINT-2724: default coroutine stack size too small on Windows. // Previously we used // boost::context::guarded_stack_allocator::default_stacksize(); - // empirically this is 64KB on Windows and Linux. Try quadrupling. + // empirically this is insufficient. #if ADDRESS_SIZE == 64 mStackSize(512*1024) #else mStackSize(256*1024) #endif { - // Register our cleanup() method for "mainloop" ticks - LLEventPumps::instance().obtain("mainloop").listen( - "LLCoros", boost::bind(&LLCoros::cleanup, this, _1)); -} - -bool LLCoros::cleanup(const LLSD&) -{ - static std::string previousName; - static int previousCount = 0; - // Walk the mCoros map, checking and removing completed coroutines. - for (CoroMap::iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ) - { - // Has this coroutine exited (normal return, exception, exit() call) - // since last tick? - if (mi->second->mCoro.exited()) - { - if (previousName != mi->first) - { - previousName = mi->first; - previousCount = 1; - } - else - { - ++previousCount; - } - - if ((previousCount < 5) || !(previousCount % 50)) - { - if (previousCount < 5) - LL_DEBUGS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; - else - LL_DEBUGS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << LL_ENDL; - - } - // The erase() call will invalidate its passed iterator value -- - // so increment mi FIRST -- but pass its original value to - // erase(). This is what postincrement is all about. - mCoros.erase(mi++); - } - else - { - // Still live, just skip this entry as if incrementing at the top - // of the loop as usual. - ++mi; - } - } - return false; } std::string LLCoros::generateDistinctName(const std::string& prefix) const { - static std::string previousName; - static int previousCount = 0; + static int unique = 0; // Allowing empty name would make getName()'s not-found return ambiguous. if (prefix.empty()) @@ -225,37 +129,15 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const // If the specified name isn't already in the map, just use that. std::string name(prefix); - // Find the lowest numeric suffix that doesn't collide with an existing - // entry. Start with 2 just to make it more intuitive for any interested - // parties: e.g. "joe", "joe2", "joe3"... - for (int i = 2; ; name = STRINGIZE(prefix << i++)) + // Until we find an unused name, append a numeric suffix for uniqueness. + while (mCoros.find(name) != mCoros.end()) { - if (mCoros.find(name) == mCoros.end()) - { - if (previousName != name) - { - previousName = name; - previousCount = 1; - } - else - { - ++previousCount; - } - - if ((previousCount < 5) || !(previousCount % 50)) - { - if (previousCount < 5) - LL_DEBUGS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; - else - LL_DEBUGS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL; - - } - - return name; - } + name = STRINGIZE(prefix << unique++); } + return name; } +/*==========================================================================*| bool LLCoros::kill(const std::string& name) { CoroMap::iterator found = mCoros.find(name); @@ -269,10 +151,11 @@ bool LLCoros::kill(const std::string& name) mCoros.erase(found); return true; } +|*==========================================================================*/ std::string LLCoros::getName() const { - return Current()->mName; + return get_CoroData("getName()").mName; } void LLCoros::setStackSize(S32 stacksize) @@ -300,6 +183,27 @@ void LLCoros::printActiveCoroutines() } } +std::string LLCoros::launch(const std::string& prefix, const callable_t& callable) +{ + std::string name(generateDistinctName(prefix)); + // 'dispatch' means: enter the new fiber immediately, returning here only + // when the fiber yields for whatever reason. + // std::allocator_arg is a flag to indicate that the following argument is + // a StackAllocator. + // protected_fixedsize_stack sets a guard page past the end of the new + // stack so that stack underflow will result in an access violation + // instead of weird, subtle, possibly undiagnosed memory stomps. + boost::fibers::fiber newCoro(boost::fibers::launch::dispatch, + std::allocator_arg, + boost::fibers::protected_fixedsize_stack(mStackSize), + [this, &name, &callable](){ toplevel(name, callable); }); + // You have two choices with a fiber instance: you can join() it or you + // can detach() it. If you try to destroy the instance before doing + // either, the program silently terminates. We don't need this handle. + newCoro.detach(); + return name; +} + #if LL_WINDOWS static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific @@ -340,10 +244,14 @@ void LLCoros::winlevel(const callable_t& callable) // Top-level wrapper around caller's coroutine callable. This function accepts // the coroutine library's implicit coro::self& parameter and saves it, but // does not pass it down to the caller's callable. -void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& callable) +void LLCoros::toplevel(const std::string& name, const callable_t& callable) { - // capture the 'self' param in CoroData - data->mSelf = &self; + CoroData* corodata = new CoroData(name); + // Store it in our pointer map. Oddly, must cast away const-ness of key. + mCoros.insert(const_cast(name), corodata); + // also set it as current + mCurrent.reset(corodata); + // run the code the caller actually wants in the coroutine try { @@ -358,70 +266,41 @@ void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& calla // Any uncaught exception derived from LLContinueError will be caught // here and logged. This coroutine will terminate but the rest of the // viewer will carry on. - LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName)); + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << corodata->mName)); } catch (...) { // Any OTHER kind of uncaught exception will cause the viewer to // crash, hopefully informatively. - CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName)); + CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << corodata->mName)); } - // This cleanup isn't perfectly symmetrical with the way we initially set - // data->mPrev, but this is our last chance to reset Current. - Current().reset(data->mPrev); } -/***************************************************************************** -* MUST BE LAST -*****************************************************************************/ -// Turn off MSVC optimizations for just LLCoros::launch() -- see -// DEV-32777. But MSVC doesn't support push/pop for optimization flags as it -// does for warning suppression, and we really don't want to force -// optimization ON for other code even in Debug or RelWithDebInfo builds. - -#if LL_MSVC -// work around broken optimizations -#pragma warning(disable: 4748) -#pragma warning(disable: 4355) // 'this' used in initializer list: yes, intentionally -#pragma optimize("", off) -#endif // LL_MSVC - -LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name, - const callable_t& callable, S32 stacksize): - mPrev(prev), +LLCoros::CoroData::CoroData(const std::string& name): mName(name), - // Wrap the caller's callable in our toplevel() function so we can manage - // Current appropriately at startup and shutdown of each coroutine. - mCoro(boost::bind(toplevel, _1, this, callable), stacksize), // don't consume events unless specifically directed mConsuming(false), - mSelf(0), mCreationTime(LLTimer::getTotalSeconds()) { } -std::string LLCoros::launch(const std::string& prefix, const callable_t& callable) +void LLCoros::delete_CoroData(CoroData* cdptr) { - std::string name(generateDistinctName(prefix)); - Current current; - // pass the current value of Current as previous context - CoroData* newCoro = new(std::nothrow) CoroData(current, name, callable, mStackSize); - if (newCoro == NULL) + // This custom cleanup function is necessarily static. Find and bind the + // LLCoros instance. + LLCoros& self(LLCoros::instance()); + // We set mCurrent on entry to a new fiber, expecting that the + // corresponding entry has already been stored in mCoros. It is an + // error if we do not find that entry. + CoroMap::iterator found = self.mCoros.find(cdptr->mName); + if (found == self.mCoros.end()) { - // Out of memory? - printActiveCoroutines(); - LL_ERRS("LLCoros") << "Failed to start coroutine: " << name << " Stacksize: " << mStackSize << " Total coroutines: " << mCoros.size() << LL_ENDL; + LL_ERRS("LLCoros") << "Coroutine '" << cdptr->mName << "' terminated " + << "without being stored in LLCoros::mCoros" + << LL_ENDL; } - // Store it in our pointer map - mCoros.insert(name, newCoro); - // also set it as current - current.reset(newCoro); - /* Run the coroutine until its first wait, then return here */ - (newCoro->mCoro)(std::nothrow); - return name; -} -#if LL_MSVC -// reenable optimizations -#pragma optimize("", on) -#endif // LL_MSVC + // Oh good, we found the mCoros entry. Erase it. Because it's a ptr_map, + // that will implicitly delete this CoroData. + self.mCoros.erase(found); +} diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index c551413811..678633497d 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -29,22 +29,13 @@ #if ! defined(LL_LLCOROS_H) #define LL_LLCOROS_H -#include -#include +#include +#include +#include #include "llsingleton.h" #include #include -#include -#include #include -#include -#include "llcoro_get_id.h" // for friend declaration - -// forward-declare helper class -namespace llcoro -{ -class Suspending; -} /** * Registry of named Boost.Coroutine instances @@ -76,19 +67,20 @@ class Suspending; * name prefix; from your prefix it generates a distinct name, registers the * new coroutine and returns the actual name. * - * The name can be used to kill off the coroutine prematurely, if needed. It - * can also provide diagnostic info: we can look up the name of the + * The name + * can provide diagnostic info: we can look up the name of the * currently-running coroutine. - * - * Finally, the next frame ("mainloop" event) after the coroutine terminates, - * LLCoros will notice its demise and destroy it. */ class LL_COMMON_API LLCoros: public LLSingleton { LLSINGLETON(LLCoros); public: - /// Canonical boost::dcoroutines::coroutine signature we use - typedef boost::dcoroutines::coroutine coro; + /// The viewer's use of the term "coroutine" became deeply embedded before + /// the industry term "fiber" emerged to distinguish userland threads from + /// simpler, more transient kinds of coroutines. Semantically they've + /// always been fibers. But at this point in history, we're pretty much + /// stuck with the term "coroutine." + typedef boost::fibers::fiber coro; /// Canonical callable type typedef boost::function callable_t; @@ -119,10 +111,10 @@ public: * DEV-32777 comments for an explanation. * * Pass a nullary callable. It works to directly pass a nullary free - * function (or static method); for all other cases use boost::bind(). Of - * course, for a non-static class method, the first parameter must be the - * class instance. Any other parameters should be passed via the bind() - * expression. + * function (or static method); for other cases use a lambda expression, + * std::bind() or boost::bind(). Of course, for a non-static class method, + * the first parameter must be the class instance. Any other parameters + * should be passed via the enclosing expression. * * launch() tweaks the suggested name so it won't collide with any * existing coroutine instance, creates the coroutine instance, registers @@ -138,7 +130,7 @@ public: * one prematurely. Returns @c true if the specified name was found and * still running at the time. */ - bool kill(const std::string& name); +// bool kill(const std::string& name); /** * From within a coroutine, look up the (tweaked) name string by which @@ -148,14 +140,18 @@ public: */ std::string getName() const; - /// for delayed initialization + /** + * For delayed initialization. To be clear, this will only affect + * coroutines launched @em after this point. The underlying facility + * provides no way to alter the stack size of any running coroutine. + */ void setStackSize(S32 stacksize); /// for delayed initialization void printActiveCoroutines(); - /// get the current coro::self& for those who really really care - static coro::self& get_self(); + /// get the current coro::id for those who really really care + static coro::id get_self(); /** * Most coroutines, most of the time, don't "consume" the events for which @@ -190,141 +186,57 @@ public: }; /** - * Please do NOT directly use boost::dcoroutines::future! It is essential - * to maintain the "current" coroutine at every context switch. This - * Future wraps the essential boost::dcoroutines::future functionality - * with that maintenance. + * Aliases for promise and future. An older underlying future implementation + * required us to wrap future; that's no longer needed. However -- if it's + * important to restore kill() functionality, we might need to provide a + * proxy, so continue using the aliases. */ template - class Future; + using Promise = boost::fibers::promise; + template + using Future = boost::fibers::future; + template + static Future getFuture(Promise& promise) { return promise.get_future(); } + + /// for data local to each running coroutine + template + using local_ptr = boost::fibers::fiber_specific_ptr; private: - friend class llcoro::Suspending; - friend llcoro::id llcoro::get_id(); std::string generateDistinctName(const std::string& prefix) const; - bool cleanup(const LLSD&); + void toplevel(const std::string& name, const callable_t& callable); struct CoroData; - static void no_cleanup(CoroData*); #if LL_WINDOWS static void winlevel(const callable_t& callable); #endif - static void toplevel(coro::self& self, CoroData* data, const callable_t& callable); - static CoroData& get_CoroData(const std::string& caller); + CoroData& get_CoroData(const std::string& caller); + const CoroData& get_CoroData(const std::string& caller) const; S32 mStackSize; // coroutine-local storage, as it were: one per coro we track struct CoroData { - CoroData(CoroData* prev, const std::string& name, - const callable_t& callable, S32 stacksize); + CoroData(const std::string& name); - // The boost::dcoroutines library supports asymmetric coroutines. Every - // time we context switch out of a coroutine, we pass control to the - // previously-active one (or to the non-coroutine stack owned by the - // thread). So our management of the "current" coroutine must be able to - // restore the previous value when we're about to switch away. - CoroData* mPrev; // tweaked name of the current coroutine const std::string mName; - // the actual coroutine instance - LLCoros::coro mCoro; // set_consuming() state bool mConsuming; - // When the dcoroutine library calls a top-level callable, it implicitly - // passes coro::self& as the first parameter. All our consumer code used - // to explicitly pass coro::self& down through all levels of call stack, - // because at the leaf level we need it for context-switching. But since - // coroutines are based on cooperative switching, we can cause the - // top-level entry point to stash a pointer to the currently-running - // coroutine, and manage it appropriately as we switch out and back in. - // That eliminates the need to pass it as an explicit parameter down - // through every level, which is unfortunately viral in nature. Finding it - // implicitly rather than explicitly allows minor maintenance in which a - // leaf-level function adds a new async I/O call that suspends the calling - // coroutine, WITHOUT having to propagate coro::self& through every - // function signature down to that point -- and of course through every - // other caller of every such function. - LLCoros::coro::self* mSelf; F64 mCreationTime; // since epoch }; typedef boost::ptr_map CoroMap; CoroMap mCoros; - // Identify the current coroutine's CoroData. Use a little helper class so - // a caller can either use a temporary instance, or instantiate a named - // variable and access it multiple times. - class Current - { - public: - Current(); - - operator LLCoros::CoroData*() { return get(); } - LLCoros::CoroData* operator->() { return get(); } - LLCoros::CoroData* get() { return mCurrent->get(); } - void reset(LLCoros::CoroData* ptr) { mCurrent->reset(ptr); } - - private: - boost::thread_specific_ptr* mCurrent; - }; -}; - -namespace llcoro -{ + // Identify the current coroutine's CoroData. This local_ptr isn't static + // because it's a member of an LLSingleton, and we rely on it being + // cleaned up in proper dependency order. + // As each coroutine terminates, use our custom cleanup function to remove + // the corresponding entry from mCoros. + local_ptr mCurrent{delete_CoroData}; -/// Instantiate one of these in a block surrounding any leaf point when -/// control literally switches away from this coroutine. -class Suspending: boost::noncopyable -{ -public: - Suspending(); - ~Suspending(); - -private: - LLCoros::CoroData* mSuspended; -}; - -} // namespace llcoro - -template -class LLCoros::Future -{ - typedef boost::dcoroutines::future dfuture; - -public: - Future(): - mFuture(get_self()) - {} - - typedef typename boost::dcoroutines::make_callback_result::type callback_t; - - callback_t make_callback() - { - return boost::dcoroutines::make_callback(mFuture); - } - -#ifndef LL_LINUX - explicit -#endif - operator bool() const - { - return bool(mFuture); - } - - bool operator!() const - { - return ! mFuture; - } - - T get() - { - // instantiate Suspending to manage the "current" coroutine - llcoro::Suspending suspended; - return *mFuture; - } - -private: - dfuture mFuture; + // Cleanup function for each fiber's instance of mCurrent. + static void delete_CoroData(CoroData* cdptr); }; #endif /* ! defined(LL_LLCOROS_H) */ diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 43e41f250d..47d99f0050 100644 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -31,18 +31,15 @@ // associated header #include "lleventcoro.h" // STL headers -#include +#include // std headers // external library headers +#include // other Linden headers #include "llsdserialize.h" #include "llsdutil.h" #include "llerror.h" #include "llcoros.h" -#include "llmake.h" -#include "llexception.h" - -#include "lleventfilter.h" namespace { @@ -105,65 +102,47 @@ void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value) llsd::drill(dest, path) = value; } -/// For LLCoros::Future::make_callback(), the callback has a signature -/// like void callback(LLSD), which isn't a valid LLEventPump listener: such -/// listeners must return bool. -template -class FutureListener -{ -public: - // FutureListener is instantiated on the coroutine stack: the stack, in - // other words, that wants to suspend. - FutureListener(const LISTENER& listener): - mListener(listener), - // Capture the suspending coroutine's flag as a consuming or - // non-consuming listener. - mConsume(LLCoros::get_consuming()) - {} - - // operator()() is called on the main stack: the stack on which the - // expected event is fired. - bool operator()(const LLSD& event) - { - mListener(event); - // tell upstream LLEventPump whether listener consumed - return mConsume; - } - -protected: - LISTENER mListener; - bool mConsume; -}; - } // anonymous void llcoro::suspend() { - // By viewer convention, we post an event on the "mainloop" LLEventPump - // each iteration of the main event-handling loop. So waiting for a single - // event on "mainloop" gives us a one-frame suspend. - suspendUntilEventOn("mainloop"); + boost::this_fiber::yield(); } void llcoro::suspendUntilTimeout(float seconds) { - LLEventTimeout timeout; - - timeout.eventAfter(seconds, LLSD()); - llcoro::suspendUntilEventOn(timeout); + // The fact that we accept non-integer seconds means we should probably + // use granularity finer than one second. However, given the overhead of + // the rest of our processing, it seems silly to use granularity finer + // than a millisecond. + boost::this_fiber::sleep_for(std::chrono::milliseconds(long(seconds * 1000))); } -LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) +namespace { - // declare the future - LLCoros::Future future; + +LLBoundListener postAndSuspendSetup(const std::string& callerName, + const std::string& listenerName, + LLCoros::Promise& promise, + const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, + const LLSD& replyPumpNamePath) +{ + // Get the consuming attribute for THIS coroutine, the one that's about to + // suspend. Don't call get_consuming() in the lambda body: that would + // return the consuming attribute for some other coroutine, most likely + // the main routine. + bool consuming(LLCoros::get_consuming()); // make a callback that will assign a value to the future, and listen on // the specified LLEventPump with that callback - std::string listenerName(listenerNameForCoro()); - LLTempBoundListener connection( + LLBoundListener connection( replyPump.getPump().listen(listenerName, - llmake(future.make_callback()))); + [&promise, consuming](const LLSD& result) + { + promise.set_value(result); + return consuming; + })); // skip the "post" part if requestPump is default-constructed if (requestPump) { @@ -171,7 +150,7 @@ LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requ // request event. LLSD modevent(event); storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName + LL_DEBUGS("lleventcoro") << callerName << ": coroutine " << listenerName << " posting to " << requestPump.getPump().getName() << LL_ENDL; @@ -179,158 +158,73 @@ LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requ // << ": " << modevent << LL_ENDL; requestPump.getPump().post(modevent); } - LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName + LL_DEBUGS("lleventcoro") << callerName << ": coroutine " << listenerName << " about to wait on LLEventPump " << replyPump.getPump().getName() << LL_ENDL; - // calling get() on the future makes us wait for it - LLSD value(future.get()); - LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName - << " resuming with " << value << LL_ENDL; - // returning should disconnect the connection - return value; -} - -LLSD llcoro::suspendUntilEventOnWithTimeout(const LLEventPumpOrPumpName& suspendPumpOrName, - F32 timeoutin, const LLSD &timeoutResult) -{ - /** - * The timeout pump is attached upstream of of the waiting pump and will - * pass the timeout event through it. We CAN NOT attach downstream since - * doing so will cause the suspendPump to fire any waiting events immediately - * and they will be lost. This becomes especially problematic with the - * LLEventTimeout(pump) constructor which will also attempt to fire those - * events using the virtual listen_impl method in the not yet fully constructed - * timeoutPump. - */ - LLEventTimeout timeoutPump; - LLEventPump &suspendPump = suspendPumpOrName.getPump(); - - LLTempBoundListener timeoutListener(timeoutPump.listen(suspendPump.getName(), - boost::bind(&LLEventPump::post, &suspendPump, _1))); - - timeoutPump.eventAfter(timeoutin, timeoutResult); - return llcoro::suspendUntilEventOn(suspendPump); + return connection; } -namespace -{ - -/** - * This helper is specifically for postAndSuspend2(). We use a single future - * object, but we want to listen on two pumps with it. Since we must still - * adapt from the callable constructed by boost::dcoroutines::make_callback() - * (void return) to provide an event listener (bool return), we've adapted - * FutureListener for the purpose. The basic idea is that we construct a - * distinct instance of FutureListener2 -- binding different instance data -- - * for each of the pumps. Then, when a pump delivers an LLSD value to either - * FutureListener2, it can combine that LLSD with its discriminator to feed - * the future object. - * - * DISCRIM is a template argument so we can use llmake() rather than - * having to write our own argument-deducing helper function. - */ -template -class FutureListener2: public FutureListener -{ - typedef FutureListener super; - -public: - // instantiated on coroutine stack: the stack about to suspend - FutureListener2(const LISTENER& listener, DISCRIM discriminator): - super(listener), - mDiscrim(discriminator) - {} - - // called on main stack: the stack on which event is fired - bool operator()(const LLSD& event) - { - // our future object is defined to accept LLEventWithID - super::mListener(LLEventWithID(event, mDiscrim)); - // tell LLEventPump whether or not event was consumed - return super::mConsume; - } - -private: - const DISCRIM mDiscrim; -}; - } // anonymous -namespace llcoro +LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) { + LLCoros::Promise promise; + std::string listenerName(listenerNameForCoro()); + + // Store connection into an LLTempBoundListener so we implicitly + // disconnect on return from this function. + LLTempBoundListener connection = + postAndSuspendSetup("postAndSuspend()", listenerName, promise, + event, requestPump, replyPump, replyPumpNamePath); -LLEventWithID postAndSuspend2(const LLSD& event, - const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump0, - const LLEventPumpOrPumpName& replyPump1, - const LLSD& replyPump0NamePath, - const LLSD& replyPump1NamePath) -{ // declare the future - LLCoros::Future future; - // either callback will assign a value to this future; listen on - // each specified LLEventPump with a callback - std::string name(listenerNameForCoro()); - LLTempBoundListener connection0( - replyPump0.getPump().listen( - name + "a", - llmake(future.make_callback(), 0))); - LLTempBoundListener connection1( - replyPump1.getPump().listen( - name + "b", - llmake(future.make_callback(), 1))); - // skip the "post" part if requestPump is default-constructed - if (requestPump) - { - // If either replyPumpNamePath is non-empty, store the corresponding - // replyPump name in the request event. - LLSD modevent(event); - storeToLLSDPath(modevent, replyPump0NamePath, - replyPump0.getPump().getName()); - storeToLLSDPath(modevent, replyPump1NamePath, - replyPump1.getPump().getName()); - LL_DEBUGS("lleventcoro") << "postAndSuspend2(): coroutine " << name - << " posting to " << requestPump.getPump().getName() - << ": " << modevent << LL_ENDL; - requestPump.getPump().post(modevent); - } - LL_DEBUGS("lleventcoro") << "postAndSuspend2(): coroutine " << name - << " about to wait on LLEventPumps " << replyPump0.getPump().getName() - << ", " << replyPump1.getPump().getName() << LL_ENDL; + LLCoros::Future future = LLCoros::getFuture(promise); // calling get() on the future makes us wait for it - LLEventWithID value(future.get()); - LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << name - << " resuming with (" << value.first << ", " << value.second << ")" - << LL_ENDL; - // returning should disconnect both connections + LLSD value(future.get()); + LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName + << " resuming with " << value << LL_ENDL; + // returning should disconnect the connection return value; } -LLSD errorException(const LLEventWithID& result, const std::string& desc) +LLSD llcoro::postAndSuspendWithTimeout(const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, + const LLSD& replyPumpNamePath, + F32 timeout, const LLSD& timeoutResult) { - // If the result arrived on the error pump (pump 1), instead of - // returning it, deliver it via exception. - if (result.second) + LLCoros::Promise promise; + std::string listenerName(listenerNameForCoro()); + + // Store connection into an LLTempBoundListener so we implicitly + // disconnect on return from this function. + LLTempBoundListener connection = + postAndSuspendSetup("postAndSuspendWithTimeout()", listenerName, promise, + event, requestPump, replyPump, replyPumpNamePath); + + // declare the future + LLCoros::Future future = LLCoros::getFuture(promise); + // wait for specified timeout + boost::fibers::future_status status = + future.wait_for(std::chrono::milliseconds(long(timeout * 1000))); + // if the future is NOT yet ready, return timeoutResult instead + if (status == boost::fibers::future_status::timeout) { - LLTHROW(LLErrorEvent(desc, result.first)); + LL_DEBUGS("lleventcoro") << "postAndSuspendWithTimeout(): coroutine " << listenerName + << " timed out after " << timeout << " seconds," + << " resuming with " << timeoutResult << LL_ENDL; + return timeoutResult; } - // That way, our caller knows a simple return must be from the reply - // pump (pump 0). - return result.first; -} - -LLSD errorLog(const LLEventWithID& result, const std::string& desc) -{ - // If the result arrived on the error pump (pump 1), log it as a fatal - // error. - if (result.second) + else { - LL_ERRS("errorLog") << desc << ":" << std::endl; - LLSDSerialize::toPrettyXML(result.first, LL_CONT); - LL_CONT << LL_ENDL; + llassert_always(status == boost::fibers::future_status::ready); + + // future is now ready, no more waiting + LLSD value(future.get()); + LL_DEBUGS("lleventcoro") << "postAndSuspendWithTimeout(): coroutine " << listenerName + << " resuming with " << value << LL_ENDL; + // returning should disconnect the connection + return value; } - // A simple return must therefore be from the reply pump (pump 0). - return result.first; } - -} // namespace llcoro diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index 84827aab4a..c0fe8b094f 100644 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -29,12 +29,8 @@ #if ! defined(LL_LLEVENTCORO_H) #define LL_LLEVENTCORO_H -#include #include -#include // std::pair #include "llevents.h" -#include "llerror.h" -#include "llexception.h" /** * Like LLListenerOrPumpName, this is a class intended for parameter lists: @@ -147,117 +143,29 @@ LLSD suspendUntilEventOn(const LLEventPumpOrPumpName& pump) return postAndSuspend(LLSD(), LLEventPumpOrPumpName(), pump); } +/// Like postAndSuspend(), but if we wait longer than @a timeout seconds, +/// stop waiting and return @a timeoutResult instead. +LLSD postAndSuspendWithTimeout(const LLSD& event, + const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, + const LLSD& replyPumpNamePath, + F32 timeout, const LLSD& timeoutResult); + /// Suspend the coroutine until an event is fired on the identified pump /// or the timeout duration has elapsed. If the timeout duration /// elapses the specified LLSD is returned. -LLSD suspendUntilEventOnWithTimeout(const LLEventPumpOrPumpName& suspendPumpOrName, F32 timeoutin, const LLSD &timeoutResult); - -} // namespace llcoro - -/// return type for two-pump variant of suspendUntilEventOn() -typedef std::pair LLEventWithID; - -namespace llcoro -{ - -/** - * This function waits for a reply on either of two specified LLEventPumps. - * Otherwise, it closely resembles postAndSuspend(); please see the documentation - * for that function for detailed parameter info. - * - * While we could have implemented the single-pump variant in terms of this - * one, there's enough added complexity here to make it worthwhile to give the - * single-pump variant its own straightforward implementation. Conversely, - * though we could use preprocessor logic to generate n-pump overloads up to - * BOOST_COROUTINE_WAIT_MAX, we don't foresee a use case. This two-pump - * overload exists because certain event APIs are defined in terms of a reply - * LLEventPump and an error LLEventPump. - * - * The LLEventWithID return value provides not only the received event, but - * the index of the pump on which it arrived (0 or 1). - * - * @note - * I'd have preferred to overload the name postAndSuspend() for both signatures. - * But consider the following ambiguous call: - * @code - * postAndSuspend(LLSD(), requestPump, replyPump, "someString"); - * @endcode - * "someString" could be converted to either LLSD (@a replyPumpNamePath for - * the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump - * function). - * - * It seems less burdensome to write postAndSuspend2() than to write either - * LLSD("someString") or LLEventOrPumpName("someString"). - */ -LLEventWithID postAndSuspend2(const LLSD& event, - const LLEventPumpOrPumpName& requestPump, - const LLEventPumpOrPumpName& replyPump0, - const LLEventPumpOrPumpName& replyPump1, - const LLSD& replyPump0NamePath=LLSD(), - const LLSD& replyPump1NamePath=LLSD()); - -/** - * Wait for the next event on either of two specified LLEventPumps. - */ inline -LLEventWithID -suspendUntilEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) +LLSD suspendUntilEventOnWithTimeout(const LLEventPumpOrPumpName& suspendPumpOrName, + F32 timeoutin, const LLSD &timeoutResult) { - // This is now a convenience wrapper for postAndSuspend2(). - return postAndSuspend2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1); + return postAndSuspendWithTimeout(LLSD(), // event + LLEventPumpOrPumpName(), // requestPump + suspendPumpOrName, // replyPump + LLSD(), // replyPumpNamePath + timeoutin, + timeoutResult); } -/** - * Helper for the two-pump variant of suspendUntilEventOn(), e.g.: - * - * @code - * LLSD reply = errorException(suspendUntilEventOn(replyPump, errorPump), - * "error response from login.cgi"); - * @endcode - * - * Examines an LLEventWithID, assuming that the second pump (pump 1) is - * listening for an error indication. If the incoming data arrived on pump 1, - * throw an LLErrorEvent exception. If the incoming data arrived on pump 0, - * just return it. Since a normal return can only be from pump 0, we no longer - * need the LLEventWithID's discriminator int; we can just return the LLSD. - * - * @note I'm not worried about introducing the (fairly generic) name - * errorException() into global namespace, because how many other overloads of - * the same name are going to accept an LLEventWithID parameter? - */ -LLSD errorException(const LLEventWithID& result, const std::string& desc); - -} // namespace llcoro - -/** - * Exception thrown by errorException(). We don't call this LLEventError - * because it's not an error in event processing: rather, this exception - * announces an event that bears error information (for some other API). - */ -class LL_COMMON_API LLErrorEvent: public LLException -{ -public: - LLErrorEvent(const std::string& what, const LLSD& data): - LLException(what), - mData(data) - {} - virtual ~LLErrorEvent() throw() {} - - LLSD getData() const { return mData; } - -private: - LLSD mData; -}; - -namespace llcoro -{ - -/** - * Like errorException(), save that this trips a fatal error using LL_ERRS - * rather than throwing an exception. - */ -LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc); - } // namespace llcoro /** @@ -304,84 +212,4 @@ private: LLEventStream mPump; }; -/** - * Other event APIs require the names of two different LLEventPumps: one for - * success response, the other for error response. Extend LLCoroEventPump - * for the two-pump use case. - */ -class LL_COMMON_API LLCoroEventPumps -{ -public: - LLCoroEventPumps(const std::string& name="coro", - const std::string& suff0="Reply", - const std::string& suff1="Error"): - mPump0(name + suff0, true), // allow tweaking the pump instance name - mPump1(name + suff1, true) - {} - /// request pump 0's name - std::string getName0() const { return mPump0.getName(); } - /// request pump 1's name - std::string getName1() const { return mPump1.getName(); } - /// request both names - std::pair getNames() const - { - return std::pair(mPump0.getName(), mPump1.getName()); - } - - /// request pump 0 - LLEventPump& getPump0() { return mPump0; } - /// request pump 1 - LLEventPump& getPump1() { return mPump1; } - - /// suspendUntilEventOn(either of our two LLEventPumps) - LLEventWithID suspend() - { - return llcoro::suspendUntilEventOn(mPump0, mPump1); - } - - /// errorException(suspend()) - LLSD suspendWithException() - { - return llcoro::errorException(suspend(), std::string("Error event on ") + getName1()); - } - - /// errorLog(suspend()) - LLSD suspendWithLog() - { - return llcoro::errorLog(suspend(), std::string("Error event on ") + getName1()); - } - - LLEventWithID postAndSuspend(const LLSD& event, - const LLEventPumpOrPumpName& requestPump, - const LLSD& replyPump0NamePath=LLSD(), - const LLSD& replyPump1NamePath=LLSD()) - { - return llcoro::postAndSuspend2(event, requestPump, mPump0, mPump1, - replyPump0NamePath, replyPump1NamePath); - } - - LLSD postAndSuspendWithException(const LLSD& event, - const LLEventPumpOrPumpName& requestPump, - const LLSD& replyPump0NamePath=LLSD(), - const LLSD& replyPump1NamePath=LLSD()) - { - return llcoro::errorException(postAndSuspend(event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); - } - - LLSD postAndSuspendWithLog(const LLSD& event, - const LLEventPumpOrPumpName& requestPump, - const LLSD& replyPump0NamePath=LLSD(), - const LLSD& replyPump1NamePath=LLSD()) - { - return llcoro::errorLog(postAndSuspend(event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); - } - -private: - LLEventStream mPump0, mPump1; -}; - #endif /* ! defined(LL_LLEVENTCORO_H) */ diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index f5f3aec270..356b896163 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -30,10 +30,9 @@ #include "llerror.h" #include "llerrorcontrol.h" // LLError::is_available() #include "lldependencies.h" -#include "llcoro_get_id.h" #include "llexception.h" +#include "llcoros.h" #include -#include #include #include // std::cerr in dire emergency #include @@ -115,19 +114,10 @@ private: // initialized, either in the constructor or in initSingleton(). However, // managing that as a stack depends on having a DISTINCT 'initializing' // stack for every C++ stack in the process! And we have a distinct C++ - // stack for every running coroutine. It would be interesting and cool to - // implement a generic coroutine-local-storage mechanism and use that - // here. The trouble is that LLCoros is itself an LLSingleton, so - // depending on LLCoros functionality could dig us into infinite - // recursion. (Moreover, when we reimplement LLCoros on top of - // Boost.Fiber, that library already provides fiber_specific_ptr -- so - // it's not worth a great deal of time and energy implementing a generic - // equivalent on top of boost::dcoroutine, which is on its way out.) - // Instead, use a map of llcoro::id to select the appropriate - // coro-specific 'initializing' stack. llcoro::get_id() is carefully - // implemented to avoid requiring LLCoros. - typedef boost::unordered_map InitializingMap; - InitializingMap mInitializing; + // stack for every running coroutine. Therefore this stack must be based + // on a coroutine-local pointer. + // This local_ptr isn't static because it's a member of an LLSingleton. + LLCoros::local_ptr mInitializing; public: // Instantiate this to obtain a reference to the coroutine-specific @@ -166,18 +156,23 @@ public: private: list_t& get_initializing_() { - // map::operator[] has find-or-create semantics, exactly what we need - // here. It returns a reference to the selected mapped_type instance. - return mInitializing[llcoro::get_id()]; + LLSingletonBase::list_t* current = mInitializing.get(); + if (! current) + { + // If the running coroutine doesn't already have an initializing + // stack, allocate a new one and save it for future reference. + current = new LLSingletonBase::list_t(); + mInitializing.reset(current); + } + return *current; } + // By the time mInitializing is destroyed, its value for every coroutine + // except the running one must have been reset() to nullptr. So every time + // we pop the list to empty, reset() the running coroutine's local_ptr. void cleanup_initializing_() { - InitializingMap::iterator found = mInitializing.find(llcoro::get_id()); - if (found != mInitializing.end()) - { - mInitializing.erase(found); - } + mInitializing.reset(nullptr); } }; diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index fa02d2bb1a..2e4b6ba823 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -26,50 +26,12 @@ * $/LicenseInfo$ */ -/*****************************************************************************/ -// test<1>() is cloned from a Boost.Coroutine example program whose copyright -// info is reproduced here: -/*---------------------------------------------------------------------------*/ -// Copyright (c) 2006, Giovanni P. Deretta -// -// This code may be used under either of the following two licences: -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. OF SUCH DAMAGE. -// -// Or: -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -/*****************************************************************************/ - #define BOOST_RESULT_OF_USE_TR1 1 -// On some platforms, Boost.Coroutine must #define magic symbols before -// #including platform-API headers. Naturally, that's ineffective unless the -// Boost.Coroutine #include is the *first* #include of the platform header. -// That means that client code must generally #include Boost.Coroutine headers -// before anything else. -#include #include #include #include #include +#include #include "linden_common.h" @@ -80,47 +42,12 @@ #include "llsd.h" #include "llsdutil.h" #include "llevents.h" -#include "tests/wrapllerrs.h" -#include "stringize.h" #include "llcoros.h" #include "lleventcoro.h" #include "../test/debug.h" using namespace llcoro; -/***************************************************************************** -* from the banana.cpp example program borrowed for test<1>() -*****************************************************************************/ -namespace coroutines = boost::dcoroutines; -using coroutines::coroutine; - -template -bool match(Iter first, Iter last, std::string match) { - std::string::iterator i = match.begin(); - for(; (first != last) && (i != match.end()); ++i) { - if (*first != *i) - return false; - ++first; - } - return i == match.end(); -} - -template -BidirectionalIterator -match_substring(BidirectionalIterator begin, - BidirectionalIterator end, - std::string xmatch, - BOOST_DEDUCED_TYPENAME coroutine::self& self) { -//BidirectionalIterator begin_ = begin; - for(; begin != end; ++begin) - if(match(begin, end, xmatch)) { - self.yield(begin); - } - return end; -} - -typedef coroutine match_coroutine_type; - /***************************************************************************** * Test helpers *****************************************************************************/ @@ -150,6 +77,8 @@ public: LLSD::Integer value(event["value"]); LLSD::String replyPumpName(event.has("fail")? "error" : "reply"); LLEventPumps::instance().obtain(event[replyPumpName]).post(value + 1); + // give listener a chance to process + llcoro::suspend(); return false; } @@ -167,51 +96,6 @@ namespace tut typedef coroutine_group::object object; coroutine_group coroutinegrp("coroutine"); - template<> template<> - void object::test<1>() - { - set_test_name("From banana.cpp example program in Boost.Coroutine distro"); - std::string buffer = "banananana"; - std::string match = "nana"; - std::string::iterator begin = buffer.begin(); - std::string::iterator end = buffer.end(); - -#if defined(BOOST_CORO_POSIX_IMPL) -// std::cout << "Using Boost.Coroutine " << BOOST_CORO_POSIX_IMPL << '\n'; -#else -// std::cout << "Using non-Posix Boost.Coroutine implementation" << std::endl; -#endif - - typedef std::string::iterator signature(std::string::iterator, - std::string::iterator, - std::string, - match_coroutine_type::self&); - - coroutine matcher - (boost::bind(static_cast(match_substring), - begin, - end, - match, - _1)); - - std::string::iterator i = matcher(); -/*==========================================================================*| - while(matcher && i != buffer.end()) { - std::cout <<"Match at: "<< std::distance(buffer.begin(), i)<<'\n'; - i = matcher(); - } -|*==========================================================================*/ - size_t matches[] = { 2, 4, 6 }; - for (size_t *mi(boost::begin(matches)), *mend(boost::end(matches)); - mi != mend; ++mi, i = matcher()) - { - ensure("more", matcher); - ensure("found", i != buffer.end()); - ensure_equals("value", std::distance(buffer.begin(), i), *mi); - } - ensure("done", ! matcher); - } - // use static data so we can intersperse coroutine functions with the // tests that engage them ImmediateAPI immediateAPI; @@ -231,7 +115,7 @@ namespace tut which = 0; } - void explicit_wait(boost::shared_ptr::callback_t>& cbp) + void explicit_wait(boost::shared_ptr>& cbp) { BEGIN { @@ -241,44 +125,40 @@ namespace tut // provides a callback-style notification (and prove that it // works). - LLCoros::Future future; - // get the callback from that future - LLCoros::Future::callback_t callback(future.make_callback()); - // Perhaps we would send a request to a remote server and arrange - // for 'callback' to be called on response. Of course that might - // involve an adapter object from the actual callback signature to - // the signature of 'callback' -- in this case, void(std::string). - // For test purposes, instead of handing 'callback' (or the + // for cbp->set_value() to be called on response. + // For test purposes, instead of handing 'callback' (or an // adapter) off to some I/O subsystem, we'll just pass it back to // our caller. - cbp.reset(new LLCoros::Future::callback_t(callback)); + cbp = boost::make_shared>(); + LLCoros::Future future = LLCoros::getFuture(*cbp); - ensure("Not yet", ! future); // calling get() on the future causes us to suspend debug("about to suspend"); stringdata = future.get(); - ensure("Got it", bool(future)); + ensure_equals("Got it", stringdata, "received"); } END } template<> template<> - void object::test<2>() + void object::test<1>() { clear(); set_test_name("explicit_wait"); DEBUG; // Construct the coroutine instance that will run explicit_wait. - boost::shared_ptr::callback_t> respond; - LLCoros::instance().launch("test<2>", + boost::shared_ptr> respond; + LLCoros::instance().launch("test<1>", boost::bind(explicit_wait, boost::ref(respond))); // When the coroutine waits for the future, it returns here. debug("about to respond"); - // Now we're the I/O subsystem delivering a result. This immediately - // transfers control back to the coroutine. - (*respond)("received"); + // Now we're the I/O subsystem delivering a result. This should make + // the coroutine ready. + respond->set_value("received"); + // but give it a chance to wake up + llcoro::suspend(); // ensure the coroutine ran and woke up again with the intended result ensure_equals(stringdata, "received"); } @@ -293,60 +173,20 @@ namespace tut } template<> template<> - void object::test<3>() + void object::test<2>() { clear(); set_test_name("waitForEventOn1"); DEBUG; - LLCoros::instance().launch("test<3>", waitForEventOn1); + LLCoros::instance().launch("test<2>", waitForEventOn1); debug("about to send"); LLEventPumps::instance().obtain("source").post("received"); + // give waitForEventOn1() a chance to run + llcoro::suspend(); debug("back from send"); ensure_equals(result.asString(), "received"); } - void waitForEventOn2() - { - BEGIN - { - LLEventWithID pair = suspendUntilEventOn("reply", "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - template<> template<> - void object::test<4>() - { - clear(); - set_test_name("waitForEventOn2 reply"); - { - DEBUG; - LLCoros::instance().launch("test<4>", waitForEventOn2); - debug("about to send"); - LLEventPumps::instance().obtain("reply").post("received"); - debug("back from send"); - } - ensure_equals(result.asString(), "received"); - ensure_equals("which pump", which, 0); - } - - template<> template<> - void object::test<5>() - { - clear(); - set_test_name("waitForEventOn2 error"); - DEBUG; - LLCoros::instance().launch("test<5>", waitForEventOn2); - debug("about to send"); - LLEventPumps::instance().obtain("error").post("badness"); - debug("back from send"); - ensure_equals(result.asString(), "badness"); - ensure_equals("which pump", which, 1); - } - void coroPump() { BEGIN @@ -359,175 +199,20 @@ namespace tut } template<> template<> - void object::test<6>() + void object::test<3>() { clear(); set_test_name("coroPump"); DEBUG; - LLCoros::instance().launch("test<6>", coroPump); - debug("about to send"); - LLEventPumps::instance().obtain(replyName).post("received"); - debug("back from send"); - ensure_equals(result.asString(), "received"); - } - - void coroPumps() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - LLEventWithID pair(waiter.suspend()); - result = pair.first; - which = pair.second; - } - END - } - - template<> template<> - void object::test<7>() - { - clear(); - set_test_name("coroPumps reply"); - DEBUG; - LLCoros::instance().launch("test<7>", coroPumps); - debug("about to send"); - LLEventPumps::instance().obtain(replyName).post("received"); - debug("back from send"); - ensure_equals(result.asString(), "received"); - ensure_equals("which pump", which, 0); - } - - template<> template<> - void object::test<8>() - { - clear(); - set_test_name("coroPumps error"); - DEBUG; - LLCoros::instance().launch("test<8>", coroPumps); - debug("about to send"); - LLEventPumps::instance().obtain(errorName).post("badness"); - debug("back from send"); - ensure_equals(result.asString(), "badness"); - ensure_equals("which pump", which, 1); - } - - void coroPumpsNoEx() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - result = waiter.suspendWithException(); - } - END - } - - template<> template<> - void object::test<9>() - { - clear(); - set_test_name("coroPumpsNoEx"); - DEBUG; - LLCoros::instance().launch("test<9>", coroPumpsNoEx); - debug("about to send"); - LLEventPumps::instance().obtain(replyName).post("received"); - debug("back from send"); - ensure_equals(result.asString(), "received"); - } - - void coroPumpsEx() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - try - { - result = waiter.suspendWithException(); - debug("no exception"); - } - catch (const LLErrorEvent& e) - { - debug(STRINGIZE("exception " << e.what())); - errordata = e.getData(); - } - } - END - } - - template<> template<> - void object::test<10>() - { - clear(); - set_test_name("coroPumpsEx"); - DEBUG; - LLCoros::instance().launch("test<10>", coroPumpsEx); - debug("about to send"); - LLEventPumps::instance().obtain(errorName).post("badness"); - debug("back from send"); - ensure("no result", result.isUndefined()); - ensure_equals("got error", errordata.asString(), "badness"); - } - - void coroPumpsNoLog() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - result = waiter.suspendWithLog(); - } - END - } - - template<> template<> - void object::test<11>() - { - clear(); - set_test_name("coroPumpsNoLog"); - DEBUG; - LLCoros::instance().launch("test<11>", coroPumpsNoLog); + LLCoros::instance().launch("test<3>", coroPump); debug("about to send"); LLEventPumps::instance().obtain(replyName).post("received"); + // give coroPump() a chance to run + llcoro::suspend(); debug("back from send"); ensure_equals(result.asString(), "received"); } - void coroPumpsLog() - { - BEGIN - { - LLCoroEventPumps waiter; - replyName = waiter.getName0(); - errorName = waiter.getName1(); - WrapLLErrs capture; - threw = capture.catch_llerrs([&waiter, &debug](){ - result = waiter.suspendWithLog(); - debug("no exception"); - }); - } - END - } - - template<> template<> - void object::test<12>() - { - clear(); - set_test_name("coroPumpsLog"); - DEBUG; - LLCoros::instance().launch("test<12>", coroPumpsLog); - debug("about to send"); - LLEventPumps::instance().obtain(errorName).post("badness"); - debug("back from send"); - ensure("no result", result.isUndefined()); - ensure_contains("got error", threw, "badness"); - } - void postAndWait1() { BEGIN @@ -541,71 +226,17 @@ namespace tut } template<> template<> - void object::test<13>() + void object::test<4>() { clear(); set_test_name("postAndWait1"); DEBUG; - LLCoros::instance().launch("test<13>", postAndWait1); + LLCoros::instance().launch("test<4>", postAndWait1); + // give postAndWait1() a chance to run + llcoro::suspend(); ensure_equals(result.asInteger(), 18); } - void postAndWait2() - { - BEGIN - { - LLEventWithID pair = ::postAndSuspend2(LLSDMap("value", 18), - immediateAPI.getPump(), - "reply2", - "error2", - "reply", - "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - template<> template<> - void object::test<14>() - { - clear(); - set_test_name("postAndWait2"); - DEBUG; - LLCoros::instance().launch("test<14>", postAndWait2); - ensure_equals(result.asInteger(), 19); - ensure_equals(which, 0); - } - - void postAndWait2_1() - { - BEGIN - { - LLEventWithID pair = ::postAndSuspend2(LLSDMap("value", 18)("fail", LLSD()), - immediateAPI.getPump(), - "reply2", - "error2", - "reply", - "error"); - result = pair.first; - which = pair.second; - debug(STRINGIZE("result = " << result << ", which = " << which)); - } - END - } - - template<> template<> - void object::test<15>() - { - clear(); - set_test_name("postAndWait2_1"); - DEBUG; - LLCoros::instance().launch("test<15>", postAndWait2_1); - ensure_equals(result.asInteger(), 19); - ensure_equals(which, 1); - } - void coroPumpPost() { BEGIN @@ -618,177 +249,14 @@ namespace tut } template<> template<> - void object::test<16>() + void object::test<5>() { clear(); set_test_name("coroPumpPost"); DEBUG; - LLCoros::instance().launch("test<16>", coroPumpPost); + LLCoros::instance().launch("test<5>", coroPumpPost); + // give coroPumpPost() a chance to run + llcoro::suspend(); ensure_equals(result.asInteger(), 18); } - - void coroPumpsPost() - { - BEGIN - { - LLCoroEventPumps waiter; - LLEventWithID pair(waiter.postAndSuspend(LLSDMap("value", 23), - immediateAPI.getPump(), "reply", "error")); - result = pair.first; - which = pair.second; - } - END - } - - template<> template<> - void object::test<17>() - { - clear(); - set_test_name("coroPumpsPost reply"); - DEBUG; - LLCoros::instance().launch("test<17>", coroPumpsPost); - ensure_equals(result.asInteger(), 24); - ensure_equals("which pump", which, 0); - } - - void coroPumpsPost_1() - { - BEGIN - { - LLCoroEventPumps waiter; - LLEventWithID pair( - waiter.postAndSuspend(LLSDMap("value", 23)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error")); - result = pair.first; - which = pair.second; - } - END - } - - template<> template<> - void object::test<18>() - { - clear(); - set_test_name("coroPumpsPost error"); - DEBUG; - LLCoros::instance().launch("test<18>", coroPumpsPost_1); - ensure_equals(result.asInteger(), 24); - ensure_equals("which pump", which, 1); - } - - void coroPumpsPostNoEx() - { - BEGIN - { - LLCoroEventPumps waiter; - result = waiter.postAndSuspendWithException(LLSDMap("value", 8), - immediateAPI.getPump(), "reply", "error"); - } - END - } - - template<> template<> - void object::test<19>() - { - clear(); - set_test_name("coroPumpsPostNoEx"); - DEBUG; - LLCoros::instance().launch("test<19>", coroPumpsPostNoEx); - ensure_equals(result.asInteger(), 9); - } - - void coroPumpsPostEx() - { - BEGIN - { - LLCoroEventPumps waiter; - try - { - result = waiter.postAndSuspendWithException( - LLSDMap("value", 9)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error"); - debug("no exception"); - } - catch (const LLErrorEvent& e) - { - debug(STRINGIZE("exception " << e.what())); - errordata = e.getData(); - } - } - END - } - - template<> template<> - void object::test<20>() - { - clear(); - set_test_name("coroPumpsPostEx"); - DEBUG; - LLCoros::instance().launch("test<20>", coroPumpsPostEx); - ensure("no result", result.isUndefined()); - ensure_equals("got error", errordata.asInteger(), 10); - } - - void coroPumpsPostNoLog() - { - BEGIN - { - LLCoroEventPumps waiter; - result = waiter.postAndSuspendWithLog(LLSDMap("value", 30), - immediateAPI.getPump(), "reply", "error"); - } - END - } - - template<> template<> - void object::test<21>() - { - clear(); - set_test_name("coroPumpsPostNoLog"); - DEBUG; - LLCoros::instance().launch("test<21>", coroPumpsPostNoLog); - ensure_equals(result.asInteger(), 31); - } - - void coroPumpsPostLog() - { - BEGIN - { - LLCoroEventPumps waiter; - WrapLLErrs capture; - threw = capture.catch_llerrs( - [&waiter, &debug](){ - result = waiter.postAndSuspendWithLog( - LLSDMap("value", 31)("fail", LLSD()), - immediateAPI.getPump(), "reply", "error"); - debug("no exception"); - }); - } - END - } - - template<> template<> - void object::test<22>() - { - clear(); - set_test_name("coroPumpsPostLog"); - DEBUG; - LLCoros::instance().launch("test<22>", coroPumpsPostLog); - ensure("no result", result.isUndefined()); - ensure_contains("got error", threw, "32"); - } } - -/*==========================================================================*| -#include - -namespace tut -{ - template<> template<> - void object::test<23>() - { - set_test_name("stacksize"); - std::cout << "default_stacksize: " << boost::context::guarded_stack_allocator::default_stacksize() << '\n'; - } -} // namespace tut -|*==========================================================================*/ diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index e0922c0667..a2a57ad740 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -217,7 +217,7 @@ target_link_libraries( ${NGHTTP2_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${LLCOREHTTP_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} rt @@ -235,7 +235,7 @@ target_link_libraries( ${NGHTTP2_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${LLCOREHTTP_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ) @@ -264,7 +264,7 @@ if (LINUX) ${LLMESSAGE_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${JSONCPP_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} rt ${GOOGLEMOCK_LIBRARIES} @@ -280,7 +280,7 @@ else (LINUX) ${LLMESSAGE_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${JSONCPP_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${GOOGLEMOCK_LIBRARIES} ) diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index 74cdff2b00..4c85dd999a 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -203,6 +203,7 @@ void LLCoprocedureManager::cancelCoprocedure(const LLUUID &id) LL_INFOS() << "Coprocedure not found." << LL_ENDL; } +/*==========================================================================*| void LLCoprocedureManager::shutdown(bool hardShutdown) { for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) @@ -211,6 +212,7 @@ void LLCoprocedureManager::shutdown(bool hardShutdown) } mPoolMap.clear(); } +|*==========================================================================*/ void LLCoprocedureManager::setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn) { @@ -303,10 +305,13 @@ LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size): LLCoprocedurePool::~LLCoprocedurePool() { +/*==========================================================================*| shutdown(); +|*==========================================================================*/ } //------------------------------------------------------------------------- +/*==========================================================================*| void LLCoprocedurePool::shutdown(bool hardShutdown) { CoroAdapterMap_t::iterator it; @@ -327,6 +332,7 @@ void LLCoprocedurePool::shutdown(bool hardShutdown) mCoroMapping.clear(); mPendingCoprocs.clear(); } +|*==========================================================================*/ //------------------------------------------------------------------------- LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoprocedurePool::CoProcedure_t proc) diff --git a/indra/llmessage/llcoproceduremanager.h b/indra/llmessage/llcoproceduremanager.h index 7d0e83180c..ba6f97355c 100644 --- a/indra/llmessage/llcoproceduremanager.h +++ b/indra/llmessage/llcoproceduremanager.h @@ -59,9 +59,11 @@ public: /// If it has not yet been dequeued it is simply removed from the queue. void cancelCoprocedure(const LLUUID &id); +/*==========================================================================*| /// Requests a shutdown of the upload manager. Passing 'true' will perform /// an immediate kill on the upload coroutine. void shutdown(bool hardShutdown = false); +|*==========================================================================*/ void setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn); diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index dd2e806dda..7b6d04b096 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -80,7 +80,7 @@ target_link_libraries(llprimitive ${LLXML_LIBRARIES} ${LLPHYSICSEXTENSIONS_LIBRARIES} ${LLCHARACTER_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ) diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index e44f57fa9f..2d2fa6588f 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -299,7 +299,7 @@ if(LL_TESTS) set(test_libs llui llmessage llcorehttp llcommon ${HUNSPELL_LIBRARY} ${LLCOMMON_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ${WINDOWS_LIBRARIES}) if(NOT LINUX) LL_ADD_INTEGRATION_TEST(llurlentry llurlentry.cpp "${test_libs}") diff --git a/indra/mac_crash_logger/CMakeLists.txt b/indra/mac_crash_logger/CMakeLists.txt index f6c4dfb59d..95637c9a28 100644 --- a/indra/mac_crash_logger/CMakeLists.txt +++ b/indra/mac_crash_logger/CMakeLists.txt @@ -77,7 +77,7 @@ target_link_libraries(mac-crash-logger ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} ${BOOST_CONTEXT_LIBRARY} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ) add_custom_command( diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index dc0d737540..45f4cb269c 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2004,7 +2004,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${viewer_LIBRARIES} ${BOOST_PROGRAM_OPTIONS_LIBRARY} ${BOOST_REGEX_LIBRARY} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${DBUSGLIB_LIBRARIES} ${OPENGL_LIBRARIES} @@ -2484,7 +2484,7 @@ if (LL_TESTS) ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} ${LIBRT_LIBRARY} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index af70751b37..db2db43ee1 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1422,6 +1422,8 @@ bool LLAppViewer::doFrame() // canonical per-frame event mainloop.post(newFrame); + // give listeners a chance to run + llcoro::suspend(); if (!LLApp::isExiting()) { diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index 8344cead57..4187076030 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -98,7 +98,7 @@ target_link_libraries(lltest ${WINDOWS_LIBRARIES} ${BOOST_PROGRAM_OPTIONS_LIBRARY} ${BOOST_REGEX_LIBRARY} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ${DL_LIBRARY} diff --git a/indra/viewer_components/login/CMakeLists.txt b/indra/viewer_components/login/CMakeLists.txt index 3bedeb7292..23518b791c 100644 --- a/indra/viewer_components/login/CMakeLists.txt +++ b/indra/viewer_components/login/CMakeLists.txt @@ -50,7 +50,7 @@ target_link_libraries(lllogin ${LLMATH_LIBRARIES} ${LLXML_LIBRARIES} ${BOOST_THREAD_LIBRARY} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ) @@ -62,7 +62,7 @@ if(LL_TESTS) set_source_files_properties( lllogin.cpp PROPERTIES - LL_TEST_ADDITIONAL_LIBRARIES "${LLMESSAGE_LIBRARIES};${LLCOREHTTP_LIBRARIES};${LLCOMMON_LIBRARIES};${BOOST_COROUTINE_LIBRARY};${BOOST_CONTEXT_LIBRARY};${BOOST_THREAD_LIBRARY};${BOOST_SYSTEM_LIBRARY}" + LL_TEST_ADDITIONAL_LIBRARIES "${LLMESSAGE_LIBRARIES};${LLCOREHTTP_LIBRARIES};${LLCOMMON_LIBRARIES};${BOOST_FIBER_LIBRARY};${BOOST_CONTEXT_LIBRARY};${BOOST_THREAD_LIBRARY};${BOOST_SYSTEM_LIBRARY}" ) LL_ADD_PROJECT_UNIT_TESTS(lllogin "${lllogin_TEST_SOURCE_FILES}") diff --git a/indra/viewer_components/login/tests/lllogin_test.cpp b/indra/viewer_components/login/tests/lllogin_test.cpp index e96c495446..774823d735 100644 --- a/indra/viewer_components/login/tests/lllogin_test.cpp +++ b/indra/viewer_components/login/tests/lllogin_test.cpp @@ -44,6 +44,7 @@ //#define DEBUG_ON #include "../../../test/debug.h" #include "llevents.h" +#include "lleventcoro.h" #include "stringize.h" #if LL_WINDOWS @@ -199,6 +200,7 @@ namespace tut credentials["passwd"] = "secret"; login.connect("login.bar.com", credentials); + llcoro::suspend(); ensure_equals("Online state", listener.lastEvent()["state"].asString(), "online"); } @@ -226,6 +228,7 @@ namespace tut credentials["passwd"] = "badpasswd"; login.connect("login.bar.com", credentials); + llcoro::suspend(); ensure_equals("Auth state", listener.lastEvent()["change"].asString(), "authenticating"); @@ -265,6 +268,7 @@ namespace tut credentials["passwd"] = "matter"; login.connect("login.bar.com", credentials); + llcoro::suspend(); ensure_equals("Auth state", listener.lastEvent()["change"].asString(), "authenticating"); @@ -300,6 +304,7 @@ namespace tut credentials["cfg_srv_timeout"] = 0.0f; login.connect("login.bar.com", credentials); + llcoro::suspend(); // Get the mainloop eventpump, which needs a pinging in order to drive the // SRV timeout. diff --git a/indra/win_crash_logger/CMakeLists.txt b/indra/win_crash_logger/CMakeLists.txt index 4fba26ab2f..1c3479bf69 100644 --- a/indra/win_crash_logger/CMakeLists.txt +++ b/indra/win_crash_logger/CMakeLists.txt @@ -83,7 +83,7 @@ target_link_libraries(windows-crash-logger ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} ${BOOST_CONTEXT_LIBRARY} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${WINDOWS_LIBRARIES} ${DXGUID_LIBRARY} ${GOOGLE_PERFTOOLS_LIBRARIES} -- cgit v1.3 From e039f5e29ee02c37d2febc21b492a0d425661897 Mon Sep 17 00:00:00 2001 From: Anchor Date: Wed, 8 May 2019 17:37:05 -0600 Subject: [DRTVWR-476] - legacy_stdio_definitions shld be the last library linked --- indra/cmake/LLAddBuildTest.cmake | 4 ++++ indra/cmake/LLCommon.cmake | 4 ---- indra/newview/CMakeLists.txt | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index ee6396e473..983b0bc3a9 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -208,6 +208,10 @@ FUNCTION(LL_ADD_INTEGRATION_TEST ${PTHREAD_LIBRARY} ) + if (WINDOWS) + list(APPEND libraries legacy_stdio_definitions) + endif (WINDOWS) + # Add test executable build target if(TEST_DEBUG) message(STATUS "ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})") diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index f56d1811d1..8900419f9b 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -33,10 +33,6 @@ else (LINUX) ${BOOST_SYSTEM_LIBRARY} ) endif (LINUX) -if (WINDOWS) - list(APPEND LLCOMMON_LIBRARIES legacy_stdio_definitions) -endif (WINDOWS) - set(LLCOMMON_LINK_SHARED OFF CACHE BOOL "Build the llcommon target as a static library.") if(LLCOMMON_LINK_SHARED) add_definitions(-DLL_COMMON_LINK_SHARED=1) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 45f4cb269c..3ba6767082 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1977,6 +1977,10 @@ endif (WINDOWS) # dumped into the target binary and runtime lookup will find the most # modern version. +if (WINDOWS) + list(APPEND LLAPPEARANCE_LIBRARIES legacy_stdio_definitions) +endif (WINDOWS) + target_link_libraries(${VIEWER_BINARY_NAME} ${PNG_PRELOAD_ARCHIVES} ${ZLIB_PRELOAD_ARCHIVES} -- cgit v1.3 From 761d9aa3bff981d1f322c9fdfe33ac35b30bf338 Mon Sep 17 00:00:00 2001 From: Anchor Date: Wed, 8 May 2019 18:05:49 -0600 Subject: [DRTVWR-476] - test adding at beginiing of list --- indra/cmake/LLAddBuildTest.cmake | 2 +- indra/newview/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/newview') diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index 983b0bc3a9..0a20a2c068 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -209,7 +209,7 @@ FUNCTION(LL_ADD_INTEGRATION_TEST ) if (WINDOWS) - list(APPEND libraries legacy_stdio_definitions) + list(INSERT libraries 0 legacy_stdio_definitions) endif (WINDOWS) # Add test executable build target diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 3ba6767082..2ad969a705 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1978,7 +1978,7 @@ endif (WINDOWS) # modern version. if (WINDOWS) - list(APPEND LLAPPEARANCE_LIBRARIES legacy_stdio_definitions) + list(INSERT PNG_PRELOAD_ARCHIVES 0 legacy_stdio_definitions) endif (WINDOWS) target_link_libraries(${VIEWER_BINARY_NAME} -- cgit v1.3 From b5bb0794f0022517c7aeff9a7775864a56488da6 Mon Sep 17 00:00:00 2001 From: Anchor Date: Wed, 8 May 2019 18:57:33 -0600 Subject: [DRTVWR-476] - fix linking --- indra/CMakeLists.txt | 5 +++++ indra/cmake/LLAddBuildTest.cmake | 5 +---- indra/integration_tests/llimage_libtest/CMakeLists.txt | 1 + indra/integration_tests/llui_libtest/CMakeLists.txt | 1 + indra/llcorehttp/CMakeLists.txt | 1 + indra/llplugin/slplugin/CMakeLists.txt | 1 + indra/newview/CMakeLists.txt | 5 +---- indra/test/CMakeLists.txt | 1 + indra/win_crash_logger/CMakeLists.txt | 2 ++ 9 files changed, 14 insertions(+), 8 deletions(-) (limited to 'indra/newview') diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 62a8f3f003..53e5d7b6a5 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -15,6 +15,11 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") include(Variables) include(BuildVersion) +set(LEGACY_STDIO_LIBS) +if (WINDOWS) + set(LEGACY_STDIO_LIBS legacy_stdio_definitions) +endif (WINDOWS) + if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type. One of: Debug Release RelWithDebInfo" FORCE) diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index 0a20a2c068..4932e9044f 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -200,6 +200,7 @@ FUNCTION(LL_ADD_INTEGRATION_TEST ) SET(libraries + ${LEGACY_STDIO_LIBS} ${library_dependencies} ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} @@ -208,10 +209,6 @@ FUNCTION(LL_ADD_INTEGRATION_TEST ${PTHREAD_LIBRARY} ) - if (WINDOWS) - list(INSERT libraries 0 legacy_stdio_definitions) - endif (WINDOWS) - # Add test executable build target if(TEST_DEBUG) message(STATUS "ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})") diff --git a/indra/integration_tests/llimage_libtest/CMakeLists.txt b/indra/integration_tests/llimage_libtest/CMakeLists.txt index d9353f904c..5787d4d600 100644 --- a/indra/integration_tests/llimage_libtest/CMakeLists.txt +++ b/indra/integration_tests/llimage_libtest/CMakeLists.txt @@ -64,6 +64,7 @@ endif (DARWIN) # Libraries on which this application depends on # Sort by high-level to low-level target_link_libraries(llimage_libtest + ${LEGACY_STDIO_LIBS} ${LLCOMMON_LIBRARIES} ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt index 34e34c7e47..1cec660eb0 100644 --- a/indra/integration_tests/llui_libtest/CMakeLists.txt +++ b/indra/integration_tests/llui_libtest/CMakeLists.txt @@ -75,6 +75,7 @@ endif (DARWIN) # Libraries on which this library depends, needed for Linux builds # Sort by high-level to low-level target_link_libraries(llui_libtest + ${LEGACY_STDIO_LIBS} llui llinventory llmessage diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt index 9dbc6f447e..9e9685f1cb 100644 --- a/indra/llcorehttp/CMakeLists.txt +++ b/indra/llcorehttp/CMakeLists.txt @@ -198,6 +198,7 @@ endif (DARWIN) ) set(example_libs + ${LEGACY_STDIO_LIBS} ${LLCOREHTTP_LIBRARIES} ${WINDOWS_LIBRARIES} ${LLMESSAGE_LIBRARIES} diff --git a/indra/llplugin/slplugin/CMakeLists.txt b/indra/llplugin/slplugin/CMakeLists.txt index 33520ad64c..e4f64448c5 100644 --- a/indra/llplugin/slplugin/CMakeLists.txt +++ b/indra/llplugin/slplugin/CMakeLists.txt @@ -63,6 +63,7 @@ set_target_properties(SLPlugin endif () target_link_libraries(SLPlugin + ${LEGACY_STDIO_LIBS} ${LLPLUGIN_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLCOMMON_LIBRARIES} diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 2ad969a705..0ba3b07566 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1977,11 +1977,8 @@ endif (WINDOWS) # dumped into the target binary and runtime lookup will find the most # modern version. -if (WINDOWS) - list(INSERT PNG_PRELOAD_ARCHIVES 0 legacy_stdio_definitions) -endif (WINDOWS) - target_link_libraries(${VIEWER_BINARY_NAME} + ${LEGACY_STDIO_LIBS} ${PNG_PRELOAD_ARCHIVES} ${ZLIB_PRELOAD_ARCHIVES} ${URIPARSER_PRELOAD_ARCHIVES} diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index 4187076030..0f14862cba 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -83,6 +83,7 @@ list(APPEND test_SOURCE_FILES ${test_HEADER_FILES}) add_executable(lltest ${test_SOURCE_FILES}) target_link_libraries(lltest + ${LEGACY_STDIO_LIBS} ${LLDATABASE_LIBRARIES} ${LLINVENTORY_LIBRARIES} ${LLMESSAGE_LIBRARIES} diff --git a/indra/win_crash_logger/CMakeLists.txt b/indra/win_crash_logger/CMakeLists.txt index 1c3479bf69..1b038c26b5 100644 --- a/indra/win_crash_logger/CMakeLists.txt +++ b/indra/win_crash_logger/CMakeLists.txt @@ -72,7 +72,9 @@ find_library(DXGUID_LIBRARY dxguid ${DIRECTX_LIBRARY_DIR}) add_executable(windows-crash-logger WIN32 ${win_crash_logger_SOURCE_FILES}) + target_link_libraries(windows-crash-logger + ${LEGACY_STDIO_LIBS} ${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES} ${LLCRASHLOGGER_LIBRARIES} ${LLWINDOW_LIBRARIES} -- cgit v1.3 From b09770946a8ae3396517c6796d1bff2a1707de26 Mon Sep 17 00:00:00 2001 From: Anchor Date: Wed, 15 May 2019 03:57:10 -0600 Subject: [DRTVWR-476] - disable dbghelp.h warnings --- autobuild.xml | 2 +- build.sh | 1 - indra/llcommon/StackWalker.cpp | 3 +++ indra/newview/llwindebug.h | 4 ++++ 4 files changed, 8 insertions(+), 2 deletions(-) (limited to 'indra/newview') diff --git a/autobuild.xml b/autobuild.xml index 8afc8db46b..a9525e6bbc 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3755,7 +3755,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors windows build_directory - build-vc${AUTOBUILD_VSVER|120}-$AUTOBUILD_ADDRSIZE + build-vc${AUTOBUILD_VSVER|150}-$AUTOBUILD_ADDRSIZE configurations RelWithDebInfo diff --git a/build.sh b/build.sh index bafee86206..545c913f92 100755 --- a/build.sh +++ b/build.sh @@ -140,7 +140,6 @@ pre_build() "$autobuild" configure --quiet -c $variant -- \ -DPACKAGE:BOOL=ON \ -DHAVOK:BOOL="$HAVOK" \ - -DCMAKE_SYSTEM_VERSION:STRING="10.0" \ -DRELEASE_CRASH_REPORTING:BOOL="$RELEASE_CRASH_REPORTING" \ -DVIEWER_SYMBOL_FILE:STRING="${VIEWER_SYMBOL_FILE:-}" \ -DBUGSPLAT_DB:STRING="${BUGSPLAT_DB:-}" \ diff --git a/indra/llcommon/StackWalker.cpp b/indra/llcommon/StackWalker.cpp index d34ad9a369..7a7230db4b 100644 --- a/indra/llcommon/StackWalker.cpp +++ b/indra/llcommon/StackWalker.cpp @@ -98,7 +98,10 @@ // If VC7 and later, then use the shipped 'dbghelp.h'-file #pragma pack(push,8) #if _MSC_VER >= 1300 +#pragma warning (push) +#pragma warning (disable:4091) // a microsoft header has warnings. Very nice. #include +#pragma warning (pop) #else // inline the important dbghelp.h-declarations... typedef enum { diff --git a/indra/newview/llwindebug.h b/indra/newview/llwindebug.h index 7e5818ba1c..05f245b311 100644 --- a/indra/newview/llwindebug.h +++ b/indra/newview/llwindebug.h @@ -29,7 +29,11 @@ #include "stdtypes.h" #include "llwin32headerslean.h" + +#pragma warning (push) +#pragma warning (disable:4091) // a microsoft header has warnings. Very nice. #include +#pragma warning (pop) class LLWinDebug: public LLSingleton -- cgit v1.3 From 6ffbed484ac9bc8358db44b420a3d598d14b72ac Mon Sep 17 00:00:00 2001 From: Brad Kittenbrink Date: Wed, 1 May 2019 15:28:55 -0700 Subject: Fix stall during login by yielding when needed from the LLXXMLRPCListener's Poller. --- indra/newview/llxmlrpclistener.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp index 7bc8af4a0b..5871161032 100644 --- a/indra/newview/llxmlrpclistener.cpp +++ b/indra/newview/llxmlrpclistener.cpp @@ -43,6 +43,7 @@ // other Linden headers #include "llerror.h" +#include "lleventcoro.h" #include "stringize.h" #include "llxmlrpctransaction.h" #include "llsecapi.h" @@ -401,6 +402,8 @@ public: // whether successful or not, send reply on requested LLEventPump replyPump.post(data); + // need to wake up the loginCoro now + llcoro::suspend(); // Because mTransaction is a boost::scoped_ptr, deleting this object // frees our LLXMLRPCTransaction object. -- cgit v1.3 From 690909716bfb2e2cae3bf9c18f6ec37d3ece086e Mon Sep 17 00:00:00 2001 From: Anchor Date: Thu, 6 Jun 2019 02:49:19 -0700 Subject: [DRTVWR-476] - fix compiler error --- indra/newview/llviewerprecompiledheaders.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h index 999d9092bd..bbbacce8fa 100644 --- a/indra/newview/llviewerprecompiledheaders.h +++ b/indra/newview/llviewerprecompiledheaders.h @@ -29,6 +29,8 @@ #ifndef LL_LLVIEWERPRECOMPILEDHEADERS_H #define LL_LLVIEWERPRECOMPILEDHEADERS_H +#include "llwin32headers.h" + // This file MUST be the first one included by each .cpp file // in viewer. // It is used to precompile headers for improved build speed. -- cgit v1.3 From 32f1dfa531062071ccf090b9c3d391b274caf02b Mon Sep 17 00:00:00 2001 From: Anchor Date: Mon, 10 Jun 2019 15:56:44 -0700 Subject: [DRTVWR-476] - fix compiler errors 32 bit windows build --- indra/llcommon/tests/lleventdispatcher_test.cpp | 4 ++-- indra/llimage/llimagejpeg.cpp | 1 - indra/llmessage/llproxy.cpp | 4 ++-- indra/llprimitive/llmodel.cpp | 2 +- indra/llui/llaccordionctrl.cpp | 2 +- indra/llvfs/lldir.cpp | 2 +- indra/newview/llpaneleditwearable.cpp | 6 +++--- indra/newview/llvovolume.cpp | 2 +- 8 files changed, 11 insertions(+), 12 deletions(-) (limited to 'indra/newview') diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp index a181d5c941..efb75951be 100644 --- a/indra/llcommon/tests/lleventdispatcher_test.cpp +++ b/indra/llcommon/tests/lleventdispatcher_test.cpp @@ -436,7 +436,7 @@ namespace tut std::vector binary; for (size_t ix = 0, h = 0xaa; ix < 6; ++ix, h += 0x11) { - binary.push_back(h); + binary.push_back((U8)h); } // Full defaults arrays. We actually don't care what the LLUUID or // LLDate values are, as long as they're different from the @@ -1145,7 +1145,7 @@ namespace tut std::vector binary; for (size_t h(0x01), i(0); i < 5; h+= 0x22, ++i) { - binary.push_back(h); + binary.push_back((U8)h); } LLSD args(LLSDMap("a", LLSDArray(true)(17)(3.14)(123.456)("char*")) ("b", LLSDArray("string") diff --git a/indra/llimage/llimagejpeg.cpp b/indra/llimage/llimagejpeg.cpp index 3b1b060c02..8c89b8a705 100644 --- a/indra/llimage/llimagejpeg.cpp +++ b/indra/llimage/llimagejpeg.cpp @@ -386,7 +386,6 @@ boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo ) { self->setLastError("Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )"); LLTHROW(LLContinueError("Out of memory in LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )")); - return false; } memcpy( new_buffer, self->mOutputBuffer, self->mOutputBufferSize ); /* Flawfinder: ignore */ delete[] self->mOutputBuffer; diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index 950599217f..86bcfe6881 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -115,9 +115,9 @@ S32 LLProxy::proxyHandshake(LLHost proxy) U32 request_size = socks_username.size() + socks_password.size() + 3; char * password_auth = new char[request_size]; password_auth[0] = 0x01; - password_auth[1] = socks_username.size(); + password_auth[1] = (char)(socks_username.size()); memcpy(&password_auth[2], socks_username.c_str(), socks_username.size()); - password_auth[socks_username.size() + 2] = socks_password.size(); + password_auth[socks_username.size() + 2] = (char)(socks_password.size()); memcpy(&password_auth[socks_username.size() + 3], socks_password.c_str(), socks_password.size()); authmethod_password_reply_t password_reply; diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 37548e3fe3..a2d9b4cd9b 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -1579,7 +1579,7 @@ void LLModel::Decomposition::fromLLSD(LLSD& decomp) range = max-min; - U16 count = position.size()/6; + U16 count = (U16)(position.size()/6); for (U32 j = 0; j < count; ++j) { diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index 623f570cef..edcbc3fbb7 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -338,7 +338,7 @@ void LLAccordionCtrl::addCollapsibleCtrl(LLView* view) addChild(accordion_tab); mAccordionTabs.push_back(accordion_tab); - accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, mAccordionTabs.size() - 1) ); + accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, (S16)(mAccordionTabs.size() - 1)) ); arrange(); } diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 2076ce334e..10fbc06c61 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -1090,7 +1090,7 @@ LLDir::SepOff LLDir::needSep(const std::string& path, const std::string& name) c { // But if BOTH path and name bring a separator, we need not add one. // Moreover, we should actually skip the leading separator of 'name'. - return SepOff(false, seplen); + return SepOff(false, (unsigned short)seplen); } // Here we know that either path_ends_sep or name_starts_sep is true -- // but not both. So don't add a separator, and don't skip any characters: diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index 6573be0aaf..c601a6c210 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -778,7 +778,7 @@ BOOL LLPanelEditWearable::postBuild() LL_WARNS() << "could not get wearable dictionary entry for wearable of type: " << type << LL_ENDL; continue; } - U8 num_subparts = wearable_entry->mSubparts.size(); + U8 num_subparts = (U8)(wearable_entry->mSubparts.size()); for (U8 index = 0; index < num_subparts; ++index) { @@ -1181,7 +1181,7 @@ void LLPanelEditWearable::showWearable(LLViewerWearable* wearable, BOOL show, BO updatePanelPickerControls(type); // clear and rebuild visual param list - U8 num_subparts = wearable_entry->mSubparts.size(); + U8 num_subparts = (U8)(wearable_entry->mSubparts.size()); for (U8 index = 0; index < num_subparts; ++index) { @@ -1372,7 +1372,7 @@ void LLPanelEditWearable::updateScrollingPanelUI() const LLEditWearableDictionary::WearableEntry *wearable_entry = LLEditWearableDictionary::getInstance()->getWearable(type); llassert(wearable_entry); if (!wearable_entry) return; - U8 num_subparts = wearable_entry->mSubparts.size(); + U8 num_subparts = (U8)(wearable_entry->mSubparts.size()); LLScrollingPanelParam::sUpdateDelayFrames = 0; for (U8 index = 0; index < num_subparts; ++index) diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 0a1efd564f..fa5938df04 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -2063,7 +2063,7 @@ void LLVOVolume::setNumTEs(const U8 num_tes) } else if(old_num_tes > num_tes && mMediaImplList.size() > num_tes) //old faces removed { - U8 end = mMediaImplList.size() ; + U8 end = (U8)(mMediaImplList.size()) ; for(U8 i = num_tes; i < end ; i++) { removeMediaImpl(i) ; -- cgit v1.3 From 16b768370b8587f63231f9bbc06ab48b58a4f15e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 4 Oct 2019 08:45:00 -0400 Subject: DRTVWR-476: Fix Windows line endings --- indra/llcommon/StackWalker.cpp | 4 ++-- indra/llcommon/llstacktrace.cpp | 2 +- indra/newview/llwindebug.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'indra/newview') diff --git a/indra/llcommon/StackWalker.cpp b/indra/llcommon/StackWalker.cpp index 7a7230db4b..56defc6465 100644 --- a/indra/llcommon/StackWalker.cpp +++ b/indra/llcommon/StackWalker.cpp @@ -98,7 +98,7 @@ // If VC7 and later, then use the shipped 'dbghelp.h'-file #pragma pack(push,8) #if _MSC_VER >= 1300 -#pragma warning (push) +#pragma warning (push) #pragma warning (disable:4091) // a microsoft header has warnings. Very nice. #include #pragma warning (pop) @@ -660,7 +660,7 @@ private: pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" ); if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) ) { - // we couldn´t find all functions + // we couldn't find all functions FreeLibrary(hPsapi); return FALSE; } diff --git a/indra/llcommon/llstacktrace.cpp b/indra/llcommon/llstacktrace.cpp index 7084fe6f60..80057bf0f2 100644 --- a/indra/llcommon/llstacktrace.cpp +++ b/indra/llcommon/llstacktrace.cpp @@ -33,7 +33,7 @@ #include #include "llwin32headerslean.h" -#pragma warning (push) +#pragma warning (push) #pragma warning (disable:4091) // a microsoft header has warnings. Very nice. #include #pragma warning (pop) diff --git a/indra/newview/llwindebug.h b/indra/newview/llwindebug.h index 05f245b311..524adba652 100644 --- a/indra/newview/llwindebug.h +++ b/indra/newview/llwindebug.h @@ -29,8 +29,8 @@ #include "stdtypes.h" #include "llwin32headerslean.h" - -#pragma warning (push) + +#pragma warning (push) #pragma warning (disable:4091) // a microsoft header has warnings. Very nice. #include #pragma warning (pop) -- cgit v1.3 From 4d65539db786056cfa8e43fd841a51b5bc0c7612 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 16 Oct 2019 11:19:29 -0400 Subject: DRTVWR-476: Directly reference LLVivoxVoiceClient::mVivoxPump. The LLEventMailDrop used to communicate with the Vivox coroutine is a member of LLVivoxVoiceClient. We don't need to keep looking it up by its string name in LLEventPumps. --- indra/newview/llvoicevivox.cpp | 80 +++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 47 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 358396632c..7d9085a214 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -1038,8 +1038,6 @@ bool LLVivoxVoiceClient::provisionVoiceAccount() bool LLVivoxVoiceClient::establishVoiceConnection() { - LLEventPump &voiceConnectPump = LLEventPumps::instance().obtain("vivoxClientPump"); - if (!mVoiceEnabled && mIsInitialized) { LL_WARNS("Voice") << "cannot establish connection; enabled "<mHandle)) ("session", "joined")); - LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + mVivoxPump.post(vivoxevent); // Add the current user as a participant here. participantStatePtr_t participant(session->addParticipant(sipURIFromName(mAccountName))); @@ -3644,7 +3630,7 @@ void LLVivoxVoiceClient::leftAudioSession(const sessionStatePtr_t &session) LLSD vivoxevent(LLSDMap("handle", LLSD::String(session->mHandle)) ("session", "removed")); - LLEventPumps::instance().post("vivoxClientPump", vivoxevent); + mVivoxPump.post(vivoxevent); } } @@ -3672,7 +3658,7 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( case 1: levent["login"] = LLSD::String("account_login"); - LLEventPumps::instance().post("vivoxClientPump", levent); + mVivoxPump.post(levent); break; case 2: break; @@ -3680,7 +3666,7 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( case 3: levent["login"] = LLSD::String("account_loggingOut"); - LLEventPumps::instance().post("vivoxClientPump", levent); + mVivoxPump.post(levent); break; case 4: @@ -3693,7 +3679,7 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( case 0: levent["login"] = LLSD::String("account_logout"); - LLEventPumps::instance().post("vivoxClientPump", levent); + mVivoxPump.post(levent); break; default: @@ -3728,7 +3714,7 @@ void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, s } if (!result.isUndefined()) - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); } void LLVivoxVoiceClient::mediaStreamUpdatedEvent( @@ -6540,7 +6526,7 @@ void LLVivoxVoiceClient::accountGetSessionFontsResponse(int statusCode, const st // receiving the last one. LLSD result(LLSDMap("voice_fonts", LLSD::Boolean(true))); - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); } notifyVoiceFontObservers(); mVoiceFontsReceived = true; @@ -6691,7 +6677,7 @@ void LLVivoxVoiceClient::enablePreviewBuffer(bool enable) else result["recplay"] = "quit"; - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); if(mCaptureBufferMode && mIsInChannel) { @@ -6712,7 +6698,7 @@ void LLVivoxVoiceClient::recordPreviewBuffer() mCaptureBufferRecording = true; LLSD result(LLSDMap("recplay", "record")); - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); } void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id) @@ -6735,7 +6721,7 @@ void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id) mCaptureBufferPlaying = true; LLSD result(LLSDMap("recplay", "playback")); - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); } void LLVivoxVoiceClient::stopPreviewBuffer() @@ -6744,7 +6730,7 @@ void LLVivoxVoiceClient::stopPreviewBuffer() mCaptureBufferPlaying = false; LLSD result(LLSDMap("recplay", "quit")); - LLEventPumps::instance().post("vivoxClientPump", result); + mVivoxPump.post(result); } bool LLVivoxVoiceClient::isPreviewRecording() -- cgit v1.3 From 6805e82acdcde9a3f18681427a33a5bc7be7a0a6 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 17 Oct 2019 12:04:02 -0400 Subject: DRTVWR-476: Introduce LLEventMailDrop::discard() (instead of flush()). Overriding virtual LLEventPump::flush() for the semantic of discarding LLEventMailDrop's queued events turns out not to be such a great idea, because LLEventPumps::flush(), which calls every registered LLEventPump's flush() method, is called every mainloop tick. The first time we hit a use case in which we expected LLEventMailDrop to hold queued events across a mainloop tick, we were baffled that they were never delivered. Moving that logic to a separate method specific to LLEventMailDrop resolves that problem. Naming it discard() clarifies its intended functionality. --- indra/llcommon/llevents.cpp | 9 +++++++-- indra/llcommon/llevents.h | 3 ++- indra/newview/llvoicevivox.cpp | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 99abb333bb..186e710c43 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -602,6 +602,11 @@ LLBoundListener LLEventMailDrop::listen_impl(const std::string& name, return LLEventStream::listen_impl(name, listener, after, before); } +void LLEventMailDrop::discard() +{ + mEventHistory.clear(); + LLEventStream::flush(); +} /***************************************************************************** * LLEventQueue @@ -621,8 +626,8 @@ bool LLEventQueue::post(const LLSD& event) void LLEventQueue::flush() { - if(!mSignal) return; - + if(!mSignal) return; + // Consider the case when a given listener on this LLEventQueue posts yet // another event on the same queue. If we loop over mEventQueue directly, // we'll end up processing all those events during the same flush() call diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 3c388bf176..ce2aa2f3c9 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -610,7 +610,8 @@ public: virtual bool post(const LLSD& event) override; /// Remove any history stored in the mail drop. - virtual void flush() override { mEventHistory.clear(); LLEventStream::flush(); }; + void discard(); + protected: virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, const NameList& after, diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 7d9085a214..1141b29163 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -1488,7 +1488,7 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession) // We are about to start a whole new session. Anything that MIGHT still be in our // maildrop is going to be stale and cause us much wailing and gnashing of teeth. // Just flush it all out and start new. - mVivoxPump.flush(); + mVivoxPump.discard(); // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4 // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck. -- cgit v1.3 From 253e360062a74cbaf87c7f9dc30ef26bca1697eb Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 22 Oct 2019 16:36:16 -0400 Subject: DRTVWR-476: Mention LLApp::stepFrame() in LLAppViewer::idle() which performs "by hand" the same sequence of calls found in stepFrame(). Why not simply call stepFrame()? Hysterical reasons? --- indra/newview/llappviewer.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index db2db43ee1..1bba17dc29 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4610,6 +4610,9 @@ void LLAppViewer::idle() LLFrameTimer::updateFrameTime(); LLFrameTimer::updateFrameCount(); LLEventTimer::updateClass(); + // LLApp::stepFrame() performs the above three calls plus mRunner.run(). + // Not sure why we don't call stepFrame() here, except that LLRunner seems + // completely redundant with LLEventTimer. LLNotificationsUI::LLToast::updateClass(); LLSmoothInterpolation::updateInterpolants(); LLMortician::updateClass(); -- cgit v1.3 From 9008124d352a195616bc8e7b88b048f479cdc4c2 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 25 Oct 2019 06:55:45 -0400 Subject: DRTVWR-476: Keep coroutine-local data on toplevel()'s stack frame. Instead of heap-allocating a CoroData instance per coroutine, storing the pointer in a ptr_map and deleting it from the ptr_map once the fiber_specific_ptr for that coroutine is cleaned up -- just declare a stack instance on the top-level stack frame, the simplest C++ lifespan management. Derive CoroData from LLInstanceTracker to detect potential name collisions and to enumerate instances. Continue registering each coroutine's CoroData instance in our fiber_specific_ptr, but use a no-op deleter function. Make ~LLCoros() directly pump the fiber scheduler a few times, instead of having a special "LLApp" listener. --- indra/llcommon/llcoros.cpp | 97 +++++++++++++++++------------------------- indra/llcommon/llcoros.h | 13 ++---- indra/newview/llvoicevivox.cpp | 6 +-- 3 files changed, 45 insertions(+), 71 deletions(-) (limited to 'indra/newview') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 78a0c5d225..febe74b559 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -76,9 +76,9 @@ LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller) // It's tempting to provide a distinct name for each thread's "main // coroutine." But as getName() has always returned the empty string // to mean "not in a coroutine," empty string should suffice here. - static CoroData sMain(""); - // We need not reset() the local_ptr to this read-only data: reuse the - // same instance for every thread's main coroutine. + static thread_local CoroData sMain(""); + // We need not reset() the local_ptr to this instance; we'll simply + // find it again every time we discover that current is null. current = &sMain; } return *current; @@ -123,16 +123,36 @@ LLCoros::LLCoros(): // boost::context::guarded_stack_allocator::default_stacksize(); // empirically this is insufficient. #if ADDRESS_SIZE == 64 - mStackSize(512*1024) + mStackSize(512*1024), #else - mStackSize(256*1024) + mStackSize(256*1024), #endif + // mCurrent does NOT own the current CoroData instance -- it simply + // points to it. So initialize it with a no-op deleter. + mCurrent{ [](CoroData*){} } { } LLCoros::~LLCoros() { - printActiveCoroutines("at LLCoros destruction"); + printActiveCoroutines("at entry to ~LLCoros()"); + // Other LLApp status-change listeners do things like close + // work queues and inject the Stop exception into pending + // promises, to force coroutines waiting on those things to + // notice and terminate. The only problem is that by the time + // LLApp sets "quitting" status, the main loop has stopped + // pumping the fiber scheduler with yield() calls. A waiting + // coroutine still might not wake up until after resources on + // which it depends have been freed. Pump it a few times + // ourselves. Of course, stop pumping as soon as the last of + // the coroutines has terminated. + for (size_t count = 0; count < 10 && CoroData::instanceCount() > 0; ++count) + { + // don't use llcoro::suspend() because that module depends + // on this one + boost::this_fiber::yield(); + } + printActiveCoroutines("after pumping"); } std::string LLCoros::generateDistinctName(const std::string& prefix) const @@ -149,7 +169,7 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const std::string name(prefix); // Until we find an unused name, append a numeric suffix for uniqueness. - while (mCoros.find(name) != mCoros.end()) + while (CoroData::getInstance(name)) { name = STRINGIZE(prefix << unique++); } @@ -186,16 +206,17 @@ void LLCoros::setStackSize(S32 stacksize) void LLCoros::printActiveCoroutines(const std::string& when) { LL_INFOS("LLCoros") << "Number of active coroutines " << when - << ": " << (S32)mCoros.size() << LL_ENDL; - if (mCoros.size() > 0) + << ": " << CoroData::instanceCount() << LL_ENDL; + if (CoroData::instanceCount() > 0) { LL_INFOS("LLCoros") << "-------------- List of active coroutines ------------"; F64 time = LLTimer::getTotalSeconds(); - for (const auto& pair : mCoros) + for (auto it(CoroData::beginInstances()), end(CoroData::endInstances()); + it != end; ++it) { - F64 life_time = time - pair.second->mCreationTime; - LL_CONT << LL_NEWLINE << pair.first << ' ' << pair.second->mStatus - << " life: " << life_time; + F64 life_time = time - it->mCreationTime; + LL_CONT << LL_NEWLINE + << it->mName << ' ' << it->mStatus << " life: " << life_time; } LL_CONT << LL_ENDL; LL_INFOS("LLCoros") << "-----------------------------------------------------" << LL_ENDL; @@ -265,17 +286,10 @@ void LLCoros::winlevel(const callable_t& callable) // case, we WANT to copy both the name and the callable to our local stack! void LLCoros::toplevel(std::string name, callable_t callable) { - CoroData* corodata = new CoroData(name); - if (corodata == NULL) - { - // Out of memory? - printActiveCoroutines(); - LL_ERRS("LLCoros") << "Failed to start coroutine: " << name << " Stacksize: " << mStackSize << " Total coroutines: " << mCoros.size() << LL_ENDL; - } - // Store it in our pointer map. - mCoros.insert(name, corodata); - // also set it as current - mCurrent.reset(corodata); + // keep the CoroData on this top-level function's stack frame + CoroData corodata(name); + // set it as current + mCurrent.reset(&corodata); // run the code the caller actually wants in the coroutine try @@ -323,43 +337,10 @@ void LLCoros::checkStop() } LLCoros::CoroData::CoroData(const std::string& name): + LLInstanceTracker(name), mName(name), // don't consume events unless specifically directed mConsuming(false), mCreationTime(LLTimer::getTotalSeconds()) { } - -void LLCoros::delete_CoroData(CoroData* cdptr) -{ - // This custom cleanup function is necessarily static. Find and bind the - // LLCoros instance. - // In case the LLCoros instance has already been deleted, just warn and - // scoot. We do NOT want to reinstantiate LLCoros during shutdown! - if (wasDeleted()) - { - // The LLSingletons involved in logging may have been deleted too. - // This warning may help developers track down coroutines that have - // not yet been cleaned up. - // But cdptr is very likely a dangling pointer by this time, so don't - // try to dereference mName. - logwarns("Coroutine terminating after LLCoros instance deleted"); - return; - } - - LLCoros& self(LLCoros::instance()); - // We set mCurrent on entry to a new fiber, expecting that the - // corresponding entry has already been stored in mCoros. It is an - // error if we do not find that entry. - CoroMap::iterator found = self.mCoros.find(cdptr->mName); - if (found == self.mCoros.end()) - { - LL_ERRS("LLCoros") << "Coroutine '" << cdptr->mName << "' terminated " - << "without being stored in LLCoros::mCoros" - << LL_ENDL; - } - - // Oh good, we found the mCoros entry. Erase it. Because it's a ptr_map, - // that will implicitly delete this CoroData. - self.mCoros.erase(found); -} diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index de7b691284..7b3420cc8f 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -34,7 +34,7 @@ #include #include #include "llsingleton.h" -#include +#include "llinstancetracker.h" #include #include @@ -269,7 +269,7 @@ private: S32 mStackSize; // coroutine-local storage, as it were: one per coro we track - struct CoroData + struct CoroData: public LLInstanceTracker { CoroData(const std::string& name); @@ -281,18 +281,11 @@ private: std::string mStatus; F64 mCreationTime; // since epoch }; - typedef boost::ptr_map CoroMap; - CoroMap mCoros; // Identify the current coroutine's CoroData. This local_ptr isn't static // because it's a member of an LLSingleton, and we rely on it being // cleaned up in proper dependency order. - // As each coroutine terminates, use our custom cleanup function to remove - // the corresponding entry from mCoros. - local_ptr mCurrent{delete_CoroData}; - - // Cleanup function for each fiber's instance of mCurrent. - static void delete_CoroData(CoroData* cdptr); + local_ptr mCurrent; }; namespace llcoro diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 1141b29163..23214a6998 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -390,7 +390,7 @@ void LLVivoxVoiceClient::init(LLPumpIO *pump) // constructor will set up LLVoiceClient::getInstance() LLVivoxVoiceClient::getInstance()->mPump = pump; -// LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();", +// LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro", // boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance())); } @@ -2537,7 +2537,7 @@ void LLVivoxVoiceClient::tuningStart() mTuningMode = true; if (!mIsCoroutineActive) { - LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();", + LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro", boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance())); } else if (mIsInChannel) @@ -5132,7 +5132,7 @@ void LLVivoxVoiceClient::setVoiceEnabled(bool enabled) if (!mIsCoroutineActive) { - LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();", + LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro", boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance())); } else -- cgit v1.3 From f80b2da513b431b063c74a353d44ffc4a76fa24e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 28 Oct 2019 14:40:20 -0400 Subject: DRTVWR-476: Add diagnostic output to llviewerjoystick.cpp. Add ndof_dump_list() call, to enumerate available devices, when ndof_init_first() returns failure. Add "joystick" tags to existing LL_INFOS() (etc.) calls in llviewerjoystick.cpp to make it easier to enable and disable such log messages. Add a specialized operator<<() function to log the contents of an NDOF_Device struct. Add a couple LL_DEBUGS() calls for more visibility into library operations. --- indra/newview/llviewerjoystick.cpp | 69 ++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 14 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index e44d80b7ce..320bd04fe8 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -62,6 +62,43 @@ F32 LLViewerJoystick::sDelta[] = {0,0,0,0,0,0,0}; #define MAX_SPACENAVIGATOR_INPUT 3000.0f #define MAX_JOYSTICK_INPUT_VALUE MAX_SPACENAVIGATOR_INPUT +#if LIB_NDOF +std::ostream& operator<<(std::ostream& out, NDOF_Device* ptr) +{ + if (! ptr) + { + return out << "nullptr"; + } + out << "NDOF_Device{ "; + out << "axes ["; + const char* delim = ""; + for (short axis = 0; axis < ptr->axes_count; ++axis) + { + out << delim << ptr->axes[axis]; + delim = ", "; + } + out << "]"; + out << ", buttons ["; + delim = ""; + for (short button = 0; button < ptr->btn_count; ++button) + { + out << delim << ptr->buttons[button]; + delim = ", "; + } + out << "]"; + out << ", range " << ptr->axes_min << ':' << ptr->axes_max; + // If we don't coerce these to unsigned, they're streamed as characters, + // e.g. ctrl-A or nul. + out << ", absolute " << unsigned(ptr->absolute); + out << ", valid " << unsigned(ptr->valid); + out << ", manufacturer '" << ptr->manufacturer << "'"; + out << ", product '" << ptr->product << "'"; + out << ", private " << ptr->private_data; + out << " }"; + return out; +} +#endif // LIB_NDOF + // ----------------------------------------------------------------------------- void LLViewerJoystick::updateEnabled(bool autoenable) { @@ -107,11 +144,11 @@ NDOF_HotPlugResult LLViewerJoystick::HotPlugAddCallback(NDOF_Device *dev) LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); if (joystick->mDriverState == JDS_UNINITIALIZED) { - LL_INFOS() << "HotPlugAddCallback: will use device:" << LL_ENDL; + LL_INFOS("joystick") << "HotPlugAddCallback: will use device:" << LL_ENDL; ndof_dump(dev); joystick->mNdofDev = dev; - joystick->mDriverState = JDS_INITIALIZED; - res = NDOF_KEEP_HOTPLUGGED; + joystick->mDriverState = JDS_INITIALIZED; + res = NDOF_KEEP_HOTPLUGGED; } joystick->updateEnabled(true); return res; @@ -125,7 +162,7 @@ void LLViewerJoystick::HotPlugRemovalCallback(NDOF_Device *dev) LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); if (joystick->mNdofDev == dev) { - LL_INFOS() << "HotPlugRemovalCallback: joystick->mNdofDev=" + LL_INFOS("joystick") << "HotPlugRemovalCallback: joystick->mNdofDev=" << joystick->mNdofDev << "; removed device:" << LL_ENDL; ndof_dump(dev); joystick->mDriverState = JDS_UNINITIALIZED; @@ -193,6 +230,7 @@ void LLViewerJoystick::init(bool autoenable) { if (mNdofDev) { + LL_DEBUGS("joystick") << "ndof_create() returned: " << mNdofDev << LL_ENDL; // Different joysticks will return different ranges of raw values. // Since we want to handle every device in the same uniform way, // we initialize the mNdofDev struct and we set the range @@ -211,16 +249,19 @@ void LLViewerJoystick::init(bool autoenable) // just have the absolute values instead. mNdofDev->absolute = 1; + LL_DEBUGS("joystick") << "ndof_init_first() received: " << mNdofDev << LL_ENDL; // init & use the first suitable NDOF device found on the USB chain if (ndof_init_first(mNdofDev, NULL)) { mDriverState = JDS_UNINITIALIZED; - LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL; + LL_WARNS("joystick") << "ndof_init_first FAILED" << LL_ENDL; + ndof_dump_list(); } else { mDriverState = JDS_INITIALIZED; } + LL_DEBUGS("joystick") << "ndof_init_first() left: " << mNdofDev << LL_ENDL; } else { @@ -258,8 +299,8 @@ void LLViewerJoystick::init(bool autoenable) { // No device connected, don't change any settings } - - LL_INFOS() << "ndof: mDriverState=" << mDriverState << "; mNdofDev=" + + LL_INFOS("joystick") << "ndof: mDriverState=" << mDriverState << "; mNdofDev=" << mNdofDev << "; libinit=" << libinit << LL_ENDL; #endif } @@ -270,7 +311,7 @@ void LLViewerJoystick::terminate() #if LIB_NDOF ndof_libcleanup(); - LL_INFOS() << "Terminated connection with NDOF device." << LL_ENDL; + LL_INFOS("joystick") << "Terminated connection with NDOF device." << LL_ENDL; mDriverState = JDS_UNINITIALIZED; #endif } @@ -1075,7 +1116,7 @@ std::string LLViewerJoystick::getDescription() bool LLViewerJoystick::isLikeSpaceNavigator() const { -#if LIB_NDOF +#if LIB_NDOF return (isJoystickInitialized() && (strncmp(mNdofDev->product, "SpaceNavigator", 14) == 0 || strncmp(mNdofDev->product, "SpaceExplorer", 13) == 0 @@ -1099,10 +1140,10 @@ void LLViewerJoystick::setSNDefaults() const float platformScaleAvXZ = 2.f; const bool is_3d_cursor = true; #endif - + //gViewerWindow->alertXml("CacheWillClear"); - LL_INFOS() << "restoring SpaceNavigator defaults..." << LL_ENDL; - + LL_INFOS("joystick") << "restoring SpaceNavigator defaults..." << LL_ENDL; + gSavedSettings.setS32("JoystickAxis0", 1); // z (at) gSavedSettings.setS32("JoystickAxis1", 0); // x (slide) gSavedSettings.setS32("JoystickAxis2", 2); // y (up) @@ -1110,11 +1151,11 @@ void LLViewerJoystick::setSNDefaults() gSavedSettings.setS32("JoystickAxis4", 3); // roll gSavedSettings.setS32("JoystickAxis5", 5); // yaw gSavedSettings.setS32("JoystickAxis6", -1); - + gSavedSettings.setBOOL("Cursor3D", is_3d_cursor); gSavedSettings.setBOOL("AutoLeveling", true); gSavedSettings.setBOOL("ZoomDirect", false); - + gSavedSettings.setF32("AvatarAxisScale0", 1.f * platformScaleAvXZ); gSavedSettings.setF32("AvatarAxisScale1", 1.f * platformScaleAvXZ); gSavedSettings.setF32("AvatarAxisScale2", 1.f); -- cgit v1.3 From ec208ddfacd25409be49fbec00943c79dd24345f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 30 Oct 2019 11:18:12 -0400 Subject: DRTVWR-476: Defend against late ~LLWatchdogTimeout() calls. LLAppViewer's heap LLWatchdogTimeout might be destroyed very late -- as late as in LLAppViewer's destructor. By that time, LLAppViewer::cleanup() has already called LLSingletonBase::deleteAll(), destroying the LLWatchdog LLSingleton instance. But LLWatchdogTimeout isa LLWatchdogEntry, and ~LLWatchdogEntry() calls stop(), and stop() tries to remove that instance from LLWatchdog, thus inadvertently resurrecting the deleted LLWatchdog. Which is pointless because the resurrected LLWatchdog has never heard of the LLWatchdogTimeout instance trying to remove itself. Defend LLWatchdogEntry::stop() against the case in which LLWatchdog has already been deleted. --- indra/newview/llwatchdog.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llwatchdog.cpp b/indra/newview/llwatchdog.cpp index dd6c77ca7d..6273f10c69 100644 --- a/indra/newview/llwatchdog.cpp +++ b/indra/newview/llwatchdog.cpp @@ -91,7 +91,11 @@ void LLWatchdogEntry::start() void LLWatchdogEntry::stop() { - LLWatchdog::getInstance()->remove(this); + // this can happen very late in the shutdown sequence + if (! LLWatchdog::wasDeleted()) + { + LLWatchdog::getInstance()->remove(this); + } } // LLWatchdogTimeout -- cgit v1.3 From 6e093662571195dbca8ba13027beeeb4afe88de6 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 30 Oct 2019 12:44:31 -0400 Subject: DRTVWR-476: Update llviewerjoystick.cpp for updated libndofdev API. --- indra/newview/llviewerjoystick.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index 320bd04fe8..3d06c95080 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -145,7 +145,7 @@ NDOF_HotPlugResult LLViewerJoystick::HotPlugAddCallback(NDOF_Device *dev) if (joystick->mDriverState == JDS_UNINITIALIZED) { LL_INFOS("joystick") << "HotPlugAddCallback: will use device:" << LL_ENDL; - ndof_dump(dev); + ndof_dump(stderr, dev); joystick->mNdofDev = dev; joystick->mDriverState = JDS_INITIALIZED; res = NDOF_KEEP_HOTPLUGGED; @@ -164,7 +164,7 @@ void LLViewerJoystick::HotPlugRemovalCallback(NDOF_Device *dev) { LL_INFOS("joystick") << "HotPlugRemovalCallback: joystick->mNdofDev=" << joystick->mNdofDev << "; removed device:" << LL_ENDL; - ndof_dump(dev); + ndof_dump(stderr, dev); joystick->mDriverState = JDS_UNINITIALIZED; } joystick->updateEnabled(true); @@ -255,7 +255,7 @@ void LLViewerJoystick::init(bool autoenable) { mDriverState = JDS_UNINITIALIZED; LL_WARNS("joystick") << "ndof_init_first FAILED" << LL_ENDL; - ndof_dump_list(); + ndof_dump_list(stderr); } else { -- cgit v1.3 From 235b37bb761082b19266bc51e44ae44b21ad1188 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 31 Oct 2019 08:13:00 -0400 Subject: DRTVWR-476: Update to VS 2017 versions of runtime DLLs. Also forget obsolete references to VS 2010 runtime DLLs. --- indra/newview/CMakeLists.txt | 6 ------ indra/newview/viewer_manifest.py | 12 ++++++------ 2 files changed, 6 insertions(+), 12 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 0ba3b07566..d7e10f610b 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1791,12 +1791,6 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/Release/openjpeg.dll ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/openjpeg.dll ${SHARED_LIB_STAGING_DIR}/Debug/openjpegd.dll - ${SHARED_LIB_STAGING_DIR}/Release/msvcr100.dll - ${SHARED_LIB_STAGING_DIR}/Release/msvcp100.dll - ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/msvcr100.dll - ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/msvcp100.dll - ${SHARED_LIB_STAGING_DIR}/Debug/msvcr100d.dll - ${SHARED_LIB_STAGING_DIR}/Debug/msvcp100d.dll ${SHARED_LIB_STAGING_DIR}/Release/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libhunspell.dll ${SHARED_LIB_STAGING_DIR}/Debug/libhunspell.dll diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index a403760670..ea8c341e97 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -528,11 +528,11 @@ class WindowsManifest(ViewerManifest): # These need to be installed as a SxS assembly, currently a 'private' assembly. # See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx if self.args['configuration'].lower() == 'debug': - self.path("msvcr120d.dll") - self.path("msvcp120d.dll") + self.path("msvcr150d.dll") + self.path("msvcp150d.dll") else: - self.path("msvcr120.dll") - self.path("msvcp120.dll") + self.path("msvcr150.dll") + self.path("msvcp150.dll") # SLVoice executable with self.prefix(src=os.path.join(pkgdir, 'bin', 'release')): @@ -606,8 +606,8 @@ class WindowsManifest(ViewerManifest): # MSVC DLLs needed for CEF and have to be in same directory as plugin with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'sharedlibs', 'Release')): - self.path("msvcp120.dll") - self.path("msvcr120.dll") + self.path("msvcp150.dll") + self.path("msvcr150.dll") # CEF files common to all configurations with self.prefix(src=os.path.join(pkgdir, 'resources')): -- cgit v1.3 From e9ef66815766d87949e35498a0883286142c9665 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 31 Oct 2019 09:06:09 -0400 Subject: DRTVWR-476: Correct runtime DLL names for VS 2017. --- indra/newview/viewer_manifest.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index ea8c341e97..292b8b9122 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -528,11 +528,11 @@ class WindowsManifest(ViewerManifest): # These need to be installed as a SxS assembly, currently a 'private' assembly. # See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx if self.args['configuration'].lower() == 'debug': - self.path("msvcr150d.dll") - self.path("msvcp150d.dll") + self.path("msvcr140d.dll") + self.path("msvcp140d.dll") else: - self.path("msvcr150.dll") - self.path("msvcp150.dll") + self.path("msvcr140.dll") + self.path("msvcp140.dll") # SLVoice executable with self.prefix(src=os.path.join(pkgdir, 'bin', 'release')): @@ -606,8 +606,8 @@ class WindowsManifest(ViewerManifest): # MSVC DLLs needed for CEF and have to be in same directory as plugin with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'sharedlibs', 'Release')): - self.path("msvcp150.dll") - self.path("msvcr150.dll") + self.path("msvcp140.dll") + self.path("msvcr140.dll") # CEF files common to all configurations with self.prefix(src=os.path.join(pkgdir, 'resources')): -- cgit v1.3 From e9a75ad31ccfb2e97d5d7bab2dec680e5b52f9a5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 31 Oct 2019 09:14:43 -0400 Subject: DRTVWR-476: Throw some more Microsoft runtime DLLs at the viewer. --- indra/newview/viewer_manifest.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 292b8b9122..bff18afd53 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -531,8 +531,14 @@ class WindowsManifest(ViewerManifest): self.path("msvcr140d.dll") self.path("msvcp140d.dll") else: + # SL-12205: For reasons not yet diagnosed, an early build of + # the VS 2017 viewer requires VS 2013 runtime DLLs as well as + # VS 2017 runtime DLLs. + self.path("msvcr120.dll") + self.path("msvcp120.dll") self.path("msvcr140.dll") self.path("msvcp140.dll") + self.path("vcruntime140.dll") # SLVoice executable with self.prefix(src=os.path.join(pkgdir, 'bin', 'release')): @@ -606,8 +612,11 @@ class WindowsManifest(ViewerManifest): # MSVC DLLs needed for CEF and have to be in same directory as plugin with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'sharedlibs', 'Release')): + self.path("msvcp120.dll") + self.path("msvcr120.dll") self.path("msvcp140.dll") self.path("msvcr140.dll") + self.path("vcruntime140.dll") # CEF files common to all configurations with self.prefix(src=os.path.join(pkgdir, 'resources')): -- cgit v1.3 From 70a63ca331484575fbd6ffb432e40f6555bb8a51 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 31 Oct 2019 13:54:51 -0400 Subject: DRTVWR-476, SL-12205: Update to glod built with VS 2017 runtime libs. --- autobuild.xml | 14 ++--- indra/cmake/Copy3rdPartyLibs.cmake | 115 ++++++++++++------------------------- indra/newview/viewer_manifest.py | 17 +----- 3 files changed, 47 insertions(+), 99 deletions(-) (limited to 'indra/newview') diff --git a/autobuild.xml b/autobuild.xml index 7c330cdf8a..439b7ef963 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1088,9 +1088,9 @@ archive hash - b618532b67f253d321329cfc7a1101e0 + 014f6dca98a0964a5d9de31a9fdf3333 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44337/391697/glod-1.0pre3.531370-darwin64-531370.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45956/405375/glod-1.0pre3.532346-darwin64-532346.tar.bz2 name darwin64 @@ -1126,11 +1126,11 @@ archive hash - 3be25680814037b801481dfc80850992 + fe8215c04f959e703653a6133e142fd4 hash_algorithm md5 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44368/391838/glod-1.0pre3.531370-windows-531370.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45955/405362/glod-1.0pre3.532346-windows-532346.tar.bz2 name windows @@ -1140,16 +1140,16 @@ archive hash - c896cb1a2a5d55f2d15877f557c1311b + 77f7c414366a903e66769d7af31c42aa url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/44367/391831/glod-1.0pre3.531370-windows64-531370.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/45954/405349/glod-1.0pre3.532346-windows64-532346.tar.bz2 name windows64 version - 1.0pre3.531370 + 1.0pre3.532346 google_breakpad diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 1e33205143..c3309b2195 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -74,96 +74,57 @@ if(WINDOWS) #******************************* # Copy MS C runtime dlls, required for packaging. if (MSVC80) - list(APPEND LMSVC_VER 80) - list(APPEND LMSVC_VERDOT 8.0) + set(MSVC_VER 80) elseif (MSVC_VERSION EQUAL 1600) # VisualStudio 2010 MESSAGE(STATUS "MSVC_VERSION ${MSVC_VERSION}") elseif (MSVC_VERSION EQUAL 1800) # VisualStudio 2013, which is (sigh) VS 12 - list(APPEND LMSVC_VER 120) - list(APPEND LMSVC_VERDOT 12.0) + set(MSVC_VER 120) elseif (MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) # Visual Studio 2017 - list(APPEND LMSVC_VER 150) - list(APPEND LMSVC_VERDOT 15.0) + set(MSVC_VER 140) else (MSVC80) MESSAGE(WARNING "New MSVC_VERSION ${MSVC_VERSION} of MSVC: adapt Copy3rdPartyLibs.cmake") endif (MSVC80) - # try to copy VS2010 redist independently of system version - # maint-7360 CP - # list(APPEND LMSVC_VER 100) - # list(APPEND LMSVC_VERDOT 10.0) - - list(LENGTH LMSVC_VER count) - math(EXPR count "${count}-1") - foreach(i RANGE ${count}) - list(GET LMSVC_VER ${i} MSVC_VER) - list(GET LMSVC_VERDOT ${i} MSVC_VERDOT) - MESSAGE(STATUS "Copying redist libs for VC ${MSVC_VERDOT}") - FIND_PATH(debug_msvc_redist_path NAME msvcr${MSVC_VER}d.dll - PATHS - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\${MSVC_VERDOT}\\Setup\\VC;ProductDir]/redist/Debug_NonRedist/x86/Microsoft.VC${MSVC_VER}.DebugCRT - [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Windows;Directory]/SysWOW64 - [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Windows;Directory]/System32 - ${MSVC_DEBUG_REDIST_PATH} - NO_DEFAULT_PATH - ) - - if(EXISTS ${debug_msvc_redist_path}) - set(debug_msvc_files - msvcr${MSVC_VER}d.dll - msvcp${MSVC_VER}d.dll - ) - - copy_if_different( - ${debug_msvc_redist_path} - "${SHARED_LIB_STAGING_DIR_DEBUG}" - out_targets - ${debug_msvc_files} - ) - set(third_party_targets ${third_party_targets} ${out_targets}) + MESSAGE(STATUS "Copying redist libs for VC ${MSVC_VER}") + if(ADDRESS_SIZE EQUAL 32) + # this folder contains the 32bit DLLs.. (yes really!) + set(registry_find_path "[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Windows;Directory]/SysWOW64") + else(ADDRESS_SIZE EQUAL 32) + # this folder contains the 64bit DLLs.. (yes really!) + set(registry_find_path "[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Windows;Directory]/System32") + endif(ADDRESS_SIZE EQUAL 32) + + FIND_PATH(release_msvc_redist_path NAME msvcr${MSVC_VER}.dll + PATHS + ${registry_find_path} + NO_DEFAULT_PATH + ) - unset(debug_msvc_redist_path CACHE) - endif() + if(EXISTS ${release_msvc_redist_path}) + set(release_msvc_files + msvcp${MSVC_VER}.dll + msvcr${MSVC_VER}.dll + vcruntime${MSVC_VER}.dll + ) - if(ADDRESS_SIZE EQUAL 32) - # this folder contains the 32bit DLLs.. (yes really!) - set(registry_find_path "[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Windows;Directory]/SysWOW64") - else(ADDRESS_SIZE EQUAL 32) - # this folder contains the 64bit DLLs.. (yes really!) - set(registry_find_path "[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Windows;Directory]/System32") - endif(ADDRESS_SIZE EQUAL 32) + copy_if_different( + ${release_msvc_redist_path} + "${SHARED_LIB_STAGING_DIR_RELEASE}" + out_targets + ${release_msvc_files} + ) + set(third_party_targets ${third_party_targets} ${out_targets}) - FIND_PATH(release_msvc_redist_path NAME msvcr${MSVC_VER}.dll - PATHS - ${registry_find_path} - NO_DEFAULT_PATH + copy_if_different( + ${release_msvc_redist_path} + "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}" + out_targets + ${release_msvc_files} ) + set(third_party_targets ${third_party_targets} ${out_targets}) - if(EXISTS ${release_msvc_redist_path}) - set(release_msvc_files - msvcr${MSVC_VER}.dll - msvcp${MSVC_VER}.dll - ) - - copy_if_different( - ${release_msvc_redist_path} - "${SHARED_LIB_STAGING_DIR_RELEASE}" - out_targets - ${release_msvc_files} - ) - set(third_party_targets ${third_party_targets} ${out_targets}) - - copy_if_different( - ${release_msvc_redist_path} - "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}" - out_targets - ${release_msvc_files} - ) - set(third_party_targets ${third_party_targets} ${out_targets}) - - unset(release_msvc_redist_path CACHE) - endif() - endforeach() + unset(release_msvc_redist_path CACHE) + endif() elseif(DARWIN) set(SHARED_LIB_STAGING_DIR_DEBUG "${SHARED_LIB_STAGING_DIR}/Debug/Resources") diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index bff18afd53..57099f8ac9 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -527,18 +527,8 @@ class WindowsManifest(ViewerManifest): # These need to be installed as a SxS assembly, currently a 'private' assembly. # See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx - if self.args['configuration'].lower() == 'debug': - self.path("msvcr140d.dll") - self.path("msvcp140d.dll") - else: - # SL-12205: For reasons not yet diagnosed, an early build of - # the VS 2017 viewer requires VS 2013 runtime DLLs as well as - # VS 2017 runtime DLLs. - self.path("msvcr120.dll") - self.path("msvcp120.dll") - self.path("msvcr140.dll") - self.path("msvcp140.dll") - self.path("vcruntime140.dll") + self.path("msvcp140.dll") + self.path("vcruntime140.dll") # SLVoice executable with self.prefix(src=os.path.join(pkgdir, 'bin', 'release')): @@ -612,10 +602,7 @@ class WindowsManifest(ViewerManifest): # MSVC DLLs needed for CEF and have to be in same directory as plugin with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'sharedlibs', 'Release')): - self.path("msvcp120.dll") - self.path("msvcr120.dll") self.path("msvcp140.dll") - self.path("msvcr140.dll") self.path("vcruntime140.dll") # CEF files common to all configurations -- cgit v1.3 From 71f6f43a320e9b3538d86035f01a5279340547e5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 1 Nov 2019 10:23:31 -0400 Subject: DRTVWR-476: Annotate Mani's plea from 2009 with a suggested solution. However, this is not the right moment to perform that refactoring. --- indra/newview/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d7e10f610b..522b97f9d3 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1773,6 +1773,11 @@ if (WINDOWS) # be met. I'm looking forward to a source-code split-up project next year that will address this kind of thing. # In the meantime, if you have any ideas on how to easily maintain one list, either here or in viewer_manifest.py # and have the build deps get tracked *please* tell me about it. + # nat: https://cmake.org/cmake/help/v3.14/command/file.html + # "For example, the code + # file(STRINGS myfile.txt myfile) + # stores a list in the variable myfile in which each item is a line from the input file." + # And of course it's straightforward to read a text file in Python. set(COPY_INPUT_DEPENDENCIES # The following commented dependencies are determined at variably at build time. Can't do this here. -- cgit v1.3 From 5918620426fd81944e6d3eb2a866d2104ad4ae69 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 1 Nov 2019 10:48:38 -0400 Subject: DRTVWR-476: Make viewer_manifest.py report its own command line. That way, if there's a problem, a developer can rerun the same command. --- indra/newview/viewer_manifest.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 57099f8ac9..2b412d01b7 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1548,6 +1548,11 @@ class Linux_x86_64_Manifest(LinuxManifest): ################################################################ if __name__ == "__main__": + # Report our own command line so that, in case of trouble, a developer can + # manually rerun the same command. + print('%s \\\n%s' % + (sys.executable, + ' '.join((("'%s'" % arg) if ' ' in arg else arg) for arg in sys.argv))) extra_arguments = [ dict(name='bugsplat', description="""BugSplat database to which to post crashes, if BugSplat crash reporting is desired""", default=''), -- cgit v1.3 From c91e5e3a2417e64da6f6404481741f211768eadd Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 15 Nov 2019 10:38:23 -0500 Subject: DRTVWR-476: Remove diagnostics around 'SetFile -a V' commands. Earlier versions of macOS manifested frustrating problems in finishing the built package. Those build steps seem to have been behaving better for a few years now. Eliminate (what we fervently hope has become) a bit of ancient cruft. --- indra/newview/viewer_manifest.py | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 2b412d01b7..e647ce3efb 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1201,11 +1201,6 @@ class DarwinManifest(ViewerManifest): devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip() volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip() - if devfile != '/dev/disk1': - # adding more debugging info based upon nat's hunches to the - # logs to help track down 'SetFile -a V' failures -brad - print "WARNING: 'SetFile -a V' command below is probably gonna fail" - # Copy everything in to the mounted .dmg app_name = self.app_name() @@ -1233,21 +1228,6 @@ class DarwinManifest(ViewerManifest): # Hide the background image, DS_Store file, and volume icon file (set their "visible" bit) for f in ".VolumeIcon.icns", "background.jpg", ".DS_Store": pathname = os.path.join(volpath, f) - # We've observed mysterious "no such file" failures of the SetFile - # command, especially on the first file listed above -- yet - # subsequent inspection of the target directory confirms it's - # there. Timing problem with copy command? Try to handle. - for x in xrange(3): - if os.path.exists(pathname): - print "Confirmed existence: %r" % pathname - break - print "Waiting for %s copy command to complete (%s)..." % (f, x+1) - sys.stdout.flush() - time.sleep(1) - # If we fall out of the loop above without a successful break, oh - # well, possibly we've mistaken the nature of the problem. In any - # case, don't hang up the whole build looping indefinitely, let - # the original problem manifest by executing the desired command. self.run_command(['SetFile', '-a', 'V', pathname]) # Create the alias file (which is a resource file) from the .r -- cgit v1.3 From 582f9e156ebaf55f076edaad52fce39b79dac388 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 15 Nov 2019 10:43:17 -0500 Subject: DRTVWR-476: Have to package libhunspell dylib now, not .a lib. --- indra/newview/viewer_manifest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index e647ce3efb..94908d201a 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -952,7 +952,7 @@ class DarwinManifest(ViewerManifest): with self.prefix(src=relpkgdir, dst=""): self.path("libndofdev.dylib") - self.path("libhunspell-1.3.a") + self.path("libhunspell-*.dylib") with self.prefix(src_dst="cursors_mac"): self.path("*.tif") -- cgit v1.3 From 2a56ab44360aa49bd0df9281efc8a8a97a510013 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 22 Nov 2019 11:58:27 -0500 Subject: DRTVWR-476, SL-12197: Don't throw Stopping from main coroutine. The new LLCoros::Stop exception is intended to terminate long-lived coroutines -- not interrupt mainstream shutdown processing. Only throw it on an explicitly-launched coroutine. Make LLCoros::getName() (used by the above test) static. As with other LLCoros methods, it might be called after the LLCoros LLSingleton instance has been deleted. Requiring the caller to call instance() implies a possible need to also call wasDeleted(). Encapsulate that nuance into a static method instead. --- indra/llcommon/llcoros.cpp | 12 +++++++++++- indra/llcommon/llcoros.h | 4 ++-- indra/llcommon/lleventcoro.cpp | 2 +- indra/llmessage/llavatarnamecache.cpp | 4 ++-- indra/newview/llaccountingcostmanager.cpp | 4 ++-- indra/viewer_components/login/lllogin.cpp | 4 ++-- 6 files changed, 20 insertions(+), 10 deletions(-) (limited to 'indra/newview') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index febe74b559..5f940de52b 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -192,7 +192,8 @@ bool LLCoros::kill(const std::string& name) } |*==========================================================================*/ -std::string LLCoros::getName() const +//static +std::string LLCoros::getName() { return get_CoroData("getName()").mName; } @@ -320,12 +321,21 @@ void LLCoros::toplevel(std::string name, callable_t callable) } } +//static void LLCoros::checkStop() { if (wasDeleted()) { LLTHROW(Shutdown("LLCoros was deleted")); } + // do this AFTER the check above, because getName() depends on + // get_CoroData(), which depends on the local_ptr in our instance(). + if (getName().empty()) + { + // Our Stop exception and its subclasses are intended to stop loitering + // coroutines. Don't throw it from the main coroutine. + return; + } if (LLApp::isStopped()) { LLTHROW(Stopped("viewer is stopped")); diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 7b3420cc8f..2e4cd8ccad 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -140,7 +140,7 @@ public: * (e.g. if the coroutine was launched by hand rather than using * LLCoros::launch()). */ - std::string getName() const; + static std::string getName(); /** * For delayed initialization. To be clear, this will only affect @@ -295,7 +295,7 @@ inline std::string logname() { static std::string main("main"); - std::string name(LLCoros::instance().getName()); + std::string name(LLCoros::getName()); return name.empty()? main : name; } diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 967c4d74d8..11b6e5bb2f 100644 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -62,7 +62,7 @@ namespace std::string listenerNameForCoro() { // If this coroutine was launched by LLCoros::launch(), find that name. - std::string name(LLCoros::instance().getName()); + std::string name(LLCoros::getName()); if (! name.empty()) { return name; diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 6a287f0cc5..fbd65cc67b 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -134,7 +134,7 @@ LLAvatarNameCache::~LLAvatarNameCache() void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector agentIds) { - LL_DEBUGS("AvNameCache") << "Entering coroutine " << LLCoros::instance().getName() + LL_DEBUGS("AvNameCache") << "Entering coroutine " << LLCoros::getName() << " with url '" << url << "', requesting " << agentIds.size() << " Agent Ids" << LL_ENDL; // Check pointer that can be cleaned up by cleanupClass() @@ -188,7 +188,7 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector observerHandle) { - LL_DEBUGS("LLAccountingCostManager") << "Entering coroutine " << LLCoros::instance().getName() + LL_DEBUGS("LLAccountingCostManager") << "Entering coroutine " << LLCoros::getName() << " with url '" << url << LL_ENDL; LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); @@ -158,7 +158,7 @@ void LLAccountingCostManager::accountingCostCoro(std::string url, } catch (...) { - LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName() + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::getName() << "('" << url << "')")); throw; } diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index 1a06459318..57a7c03525 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -148,7 +148,7 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) } try { - LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::instance().getName() + LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::getName() << " with uri '" << uri << "', parameters " << printable_params << LL_ENDL; LLEventPump& xmlrpcPump(LLEventPumps::instance().obtain("LLXMLRPCTransaction")); @@ -307,7 +307,7 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) sendProgressEvent("offline", "fail.login", error_response); } catch (...) { - CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName() + CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::getName() << "('" << uri << "', " << printable_params << ")")); } } -- cgit v1.3 From 1c62f52af83aa6e6684f5d1f496d8095c7ca321f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 18 Dec 2019 15:29:23 -0500 Subject: DRTVWR-476: LLChannelManager depends on LLUI. Tell LLSingleton. --- indra/newview/llchannelmanager.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp index 0b7b9cbbc7..9e7a8ba95c 100644 --- a/indra/newview/llchannelmanager.cpp +++ b/indra/newview/llchannelmanager.cpp @@ -48,11 +48,18 @@ LLChannelManager::LLChannelManager() LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLChannelManager::onLoginCompleted, this)); mChannelList.clear(); mStartUpChannel = NULL; - + if(!gViewerWindow) { LL_ERRS() << "LLChannelManager::LLChannelManager() - viwer window is not initialized yet" << LL_ENDL; } + + // We don't actually need this instance right now, but our + // cleanupSingleton() method deletes LLScreenChannels, which need to + // unregister from LLUI. Calling LLUI::instance() here establishes the + // dependency so LLSingletonBase::deleteAll() calls our deleteSingleton() + // before LLUI::deleteSingleton(). + LLUI::instance(); } //-------------------------------------------------------------------------- -- cgit v1.3 From 9d5d257ceeb8297bcd8ac17164b6584717b5c024 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 14 May 2020 16:58:33 -0400 Subject: DRTVWR-476, SL-12204: Fix crash in Marketplace Listings. The observed crash was due to sharing a stateful global resource (the global LLMessageSystem instance) between different tasks. Specifically, a coroutine sets its mMessageReader one way, expecting that value to persist until it's done with message parsing, but another coroutine sneaks in at a suspension point and sets it differently. Introduce LockMessageReader and LockMessageChecker classes, which must be instantiated by a consumer of the resource. The constructor of each locks a coroutine-aware mutex, so that for the lifetime of the lock object no other coroutine can instantiate another. Refactor the code so that LLMessageSystem::mMessageReader can only be modified by LockMessageReader, not by direct assignment. mMessageReader is now an instance of LLMessageReaderPointer, which supports dereferencing and comparison but not assignment. Only LockMessageReader can change its value. LockMessageReader addresses the use case in which the specific mMessageReader value need only persist for the duration of a single method call. Add an instance in LLMessageHandlerBridge::post(). LockMessageChecker is a subclass of LockMessageReader: both lock the same mutex. LockMessageChecker addresses the use case in which the specific mMessageReader value must persist across multiple method calls. Modify the methods in question to require a LockMessageChecker instance. Provide LockMessageChecker forwarding methods to facilitate calling the underlying LLMessageSystem methods via the LockMessageChecker instance. Add LockMessageChecker instances to LLAppViewer::idleNetwork(), a couple cases in idle_startup() and LLMessageSystem::establishBidirectionalTrust(). --- indra/llmessage/message.cpp | 41 +++++++------ indra/llmessage/message.h | 130 ++++++++++++++++++++++++++++++++++++++++-- indra/newview/llappviewer.cpp | 49 ++++++++-------- indra/newview/llstartup.cpp | 42 ++++++++------ 4 files changed, 199 insertions(+), 63 deletions(-) (limited to 'indra/newview') diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 6ef4025ab1..da62bb12e8 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -117,8 +117,8 @@ void LLMessageHandlerBridge::post(LLHTTPNode::ResponsePtr response, gMessageSystem->mLastSender = LLHost(input["sender"].asString()); gMessageSystem->mPacketsIn += 1; gMessageSystem->mLLSDMessageReader->setMessage(namePtr, input["body"]); - gMessageSystem->mMessageReader = gMessageSystem->mLLSDMessageReader; - + LockMessageReader rdr(gMessageSystem->mMessageReader, gMessageSystem->mLLSDMessageReader); + if(gMessageSystem->callHandler(namePtr, false, gMessageSystem)) { response->result(LLSD()); @@ -189,7 +189,7 @@ void LLMessageSystem::init() mTimingCallbackData = NULL; mMessageBuilder = NULL; - mMessageReader = NULL; + LockMessageReader(mMessageReader, NULL); } // Read file and build message templates @@ -230,7 +230,6 @@ LLMessageSystem::LLMessageSystem(const std::string& filename, U32 port, mTemplateMessageReader = new LLTemplateMessageReader(mMessageNumbers); mLLSDMessageReader = new LLSDMessageReader(); - mMessageReader = NULL; // initialize various bits of net info mSocket = 0; @@ -330,7 +329,6 @@ LLMessageSystem::~LLMessageSystem() delete mTemplateMessageReader; mTemplateMessageReader = NULL; - mMessageReader = NULL; delete mTemplateMessageBuilder; mTemplateMessageBuilder = NULL; @@ -480,11 +478,12 @@ LLCircuitData* LLMessageSystem::findCircuit(const LLHost& host, } // Returns TRUE if a valid, on-circuit message has been received. -BOOL LLMessageSystem::checkMessages( S64 frame_count ) +// Requiring a non-const LockMessageChecker reference ensures that +// mMessageReader has been set to mTemplateMessageReader. +BOOL LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count ) { // Pump BOOL valid_packet = FALSE; - mMessageReader = mTemplateMessageReader; LLTransferTargetVFile::updateQueue(); @@ -748,7 +747,7 @@ S32 LLMessageSystem::getReceiveBytes() const } -void LLMessageSystem::processAcks(F32 collect_time) +void LLMessageSystem::processAcks(LockMessageChecker&, F32 collect_time) { F64Seconds mt_sec = getMessageTimeSeconds(); { @@ -2062,8 +2061,9 @@ void LLMessageSystem::dispatch( return; } // enable this for output of message names - //LL_INFOS("Messaging") << "< \"" << msg_name << "\"" << LL_ENDL; - //LL_DEBUGS() << "data: " << LLSDNotationStreamer(message) << LL_ENDL; + LL_DEBUGS("Messaging") << "< \"" << msg_name << "\"" << LL_ENDL; + LL_DEBUGS("Messaging") << "context: " << context << LL_ENDL; + LL_DEBUGS("Messaging") << "message: " << message << LL_ENDL; handler->post(responsep, context, message); } @@ -3268,6 +3268,8 @@ void null_message_callback(LLMessageSystem *msg, void **data) // up, and then sending auth messages. void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_count ) { + LockMessageChecker lmc(this); + std::string shared_secret = get_shared_secret(); if(shared_secret.empty()) { @@ -3287,7 +3289,7 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_ addU8Fast(_PREHASH_PingID, 0); addU32Fast(_PREHASH_OldestUnacked, 0); sendMessage(host); - if (checkMessages( frame_count )) + if (lmc.checkMessages( frame_count )) { if (isMessageFast(_PREHASH_CompletePingCheck) && (getSender() == host)) @@ -3295,7 +3297,7 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_ break; } } - processAcks(); + lmc.processAcks(); ms_sleep(1); } @@ -3314,8 +3316,8 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_ cdp = mCircuitInfo.findCircuit(host); if(!cdp) break; // no circuit anymore, no point continuing. if(cdp->getTrusted()) break; // circuit is trusted. - checkMessages(frame_count); - processAcks(); + lmc.checkMessages(frame_count); + lmc.processAcks(); ms_sleep(1); } } @@ -3973,11 +3975,18 @@ void LLMessageSystem::setTimeDecodesSpamThreshold( F32 seconds ) LLMessageReader::setTimeDecodesSpamThreshold(seconds); } +LockMessageChecker::LockMessageChecker(LLMessageSystem* msgsystem): + // for the lifespan of this LockMessageChecker instance, use + // LLTemplateMessageReader as msgsystem's mMessageReader + LockMessageReader(msgsystem->mMessageReader, msgsystem->mTemplateMessageReader), + mMessageSystem(msgsystem) +{} + // HACK! babbage: return true if message rxed via either UDP or HTTP // TODO: babbage: move gServicePump in to LLMessageSystem? -bool LLMessageSystem::checkAllMessages(S64 frame_count, LLPumpIO* http_pump) +bool LLMessageSystem::checkAllMessages(LockMessageChecker& lmc, S64 frame_count, LLPumpIO* http_pump) { - if(checkMessages(frame_count)) + if(lmc.checkMessages(frame_count)) { return true; } diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index 0af5a1b96d..a3f2829ece 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -61,6 +61,8 @@ #include "llstoredmessage.h" #include "boost/function.hpp" #include "llpounceable.h" +#include "llcoros.h" +#include LLCOROS_MUTEX_HEADER const U32 MESSAGE_MAX_STRINGS_LENGTH = 64; const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192; @@ -199,6 +201,89 @@ public: virtual void complete(const LLHost& host, const LLUUID& agent) const = 0; }; +/** + * SL-12204: We've observed crashes when consumer code sets + * LLMessageSystem::mMessageReader, assuming that all subsequent processing of + * the current message will use the same mMessageReader value -- only to have + * a different coroutine sneak in and replace mMessageReader before + * completion. This is a limitation of sharing a stateful global resource for + * message parsing; instead code receiving a new message should instantiate a + * (trivially constructed) local message parser and use that. + * + * Until then, when one coroutine sets a particular LLMessageReader subclass + * as the current message reader, ensure that no other coroutine can replace + * it until the first coroutine has finished with its message. + * + * This is achieved with two helper classes. LLMessageSystem::mMessageReader + * is now an LLMessageReaderPointer instance, which can efficiently compare or + * dereference its contained LLMessageReader* but which cannot be directly + * assigned. To change the value of LLMessageReaderPointer, you must + * instantiate LockMessageReader with the LLMessageReader* you wish to make + * current. mMessageReader will have that value for the lifetime of the + * LockMessageReader instance, then revert to nullptr. Moreover, as its name + * implies, LockMessageReader locks the mutex in LLMessageReaderPointer so + * that any other coroutine instantiating LockMessageReader will block until + * the first coroutine has destroyed its instance. + */ +class LLMessageReaderPointer +{ +public: + LLMessageReaderPointer(): mPtr(nullptr) {} + // It is essential that comparison and dereferencing must be fast, which + // is why we don't check for nullptr when dereferencing. + LLMessageReader* operator->() const { return mPtr; } + bool operator==(const LLMessageReader* other) const { return mPtr == other; } + bool operator!=(const LLMessageReader* other) const { return ! (*this == other); } +private: + // Only LockMessageReader can set mPtr. + friend class LockMessageReader; + LLMessageReader* mPtr; + LLCoros::Mutex mMutex; +}; + +/** + * To set mMessageReader to nullptr: + * + * @code + * // use an anonymous instance that is destroyed immediately + * LockMessageReader(gMessageSystem->mMessageReader, nullptr); + * @endcode + * + * Why do we still require going through LockMessageReader at all? Because it + * would be Bad if any coroutine set mMessageReader to nullptr while another + * coroutine was still parsing a message. + */ +class LockMessageReader +{ +public: + // Because LockMessageReader contains LLCoros::LockType, it is already + // move-only. No need to delete the copy constructor or copy assignment. + LockMessageReader(LLMessageReaderPointer& var, LLMessageReader* instance): + mVar(var.mPtr), + mLock(var.mMutex) + { + mVar = instance; + } + ~LockMessageReader() + { + mVar = nullptr; + } +private: + // capture a reference to LLMessageReaderPointer::mPtr + decltype(LLMessageReaderPointer::mPtr)& mVar; + // while holding a lock on LLMessageReaderPointer::mMutex + LLCoros::LockType mLock; +}; + +/** + * LockMessageReader is great as long as you only need mMessageReader locked + * during a single LLMessageSystem function call. However, empirically the + * sequence from checkAllMessages() through processAcks() need mMessageReader + * locked to LLTemplateMessageReader. Enforce that by making them require an + * instance of LockMessageChecker. + */ +class LockMessageChecker; + class LLMessageSystem : public LLMessageSenderInterface { private: @@ -331,8 +416,8 @@ public: bool addCircuitCode(U32 code, const LLUUID& session_id); BOOL poll(F32 seconds); // Number of seconds that we want to block waiting for data, returns if data was received - BOOL checkMessages( S64 frame_count = 0 ); - void processAcks(F32 collect_time = 0.f); + BOOL checkMessages(LockMessageChecker&, S64 frame_count = 0 ); + void processAcks(LockMessageChecker&, F32 collect_time = 0.f); BOOL isMessageFast(const char *msg); BOOL isMessage(const char *msg) @@ -730,7 +815,7 @@ public: const LLSD& data); // Check UDP messages and pump http_pump to receive HTTP messages. - bool checkAllMessages(S64 frame_count, LLPumpIO* http_pump); + bool checkAllMessages(LockMessageChecker&, S64 frame_count, LLPumpIO* http_pump); // Moved to allow access from LLTemplateMessageDispatcher void clearReceiveState(); @@ -817,12 +902,13 @@ private: LLMessageBuilder* mMessageBuilder; LLTemplateMessageBuilder* mTemplateMessageBuilder; LLSDMessageBuilder* mLLSDMessageBuilder; - LLMessageReader* mMessageReader; + LLMessageReaderPointer mMessageReader; LLTemplateMessageReader* mTemplateMessageReader; LLSDMessageReader* mLLSDMessageReader; friend class LLMessageHandlerBridge; - + friend class LockMessageChecker; + bool callHandler(const char *name, bool trustedSource, LLMessageSystem* msg); @@ -835,6 +921,40 @@ private: // external hook into messaging system extern LLPounceable gMessageSystem; +// Implementation of LockMessageChecker depends on definition of +// LLMessageSystem, hence must follow it. +class LockMessageChecker: public LockMessageReader +{ +public: + LockMessageChecker(LLMessageSystem* msgsystem); + + // For convenience, provide forwarding wrappers so you can call (e.g.) + // checkAllMessages() on your LockMessageChecker instance instead of + // passing the instance to LLMessageSystem::checkAllMessages(). Use + // perfect forwarding to avoid having to maintain these wrappers in sync + // with the target methods. + template + bool checkAllMessages(ARGS&&... args) + { + return mMessageSystem->checkAllMessages(*this, std::forward(args)...); + } + + template + bool checkMessages(ARGS&&... args) + { + return mMessageSystem->checkMessages(*this, std::forward(args)...); + } + + template + void processAcks(ARGS&&... args) + { + return mMessageSystem->processAcks(*this, std::forward(args)...); + } + +private: + LLMessageSystem* mMessageSystem; +}; + // Must specific overall system version, which is used to determine // if a patch is available in the message template checksum verification. // Return true if able to initialize system. diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 65bfcec8c4..70d3c10524 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5250,37 +5250,40 @@ void LLAppViewer::idleNetwork() const S64 frame_count = gFrameCount; // U32->S64 F32 total_time = 0.0f; - while (gMessageSystem->checkAllMessages(frame_count, gServicePump)) { - if (gDoDisconnect) + LockMessageChecker lmc(gMessageSystem); + while (lmc.checkAllMessages(frame_count, gServicePump)) { - // We're disconnecting, don't process any more messages from the server - // We're usually disconnecting due to either network corruption or a - // server going down, so this is OK. - break; - } + if (gDoDisconnect) + { + // We're disconnecting, don't process any more messages from the server + // We're usually disconnecting due to either network corruption or a + // server going down, so this is OK. + break; + } - total_decoded++; - gPacketsIn++; + total_decoded++; + gPacketsIn++; - if (total_decoded > MESSAGE_MAX_PER_FRAME) - { - break; - } + if (total_decoded > MESSAGE_MAX_PER_FRAME) + { + break; + } #ifdef TIME_THROTTLE_MESSAGES - // Prevent slow packets from completely destroying the frame rate. - // This usually happens due to clumps of avatars taking huge amount - // of network processing time (which needs to be fixed, but this is - // a good limit anyway). - total_time = check_message_timer.getElapsedTimeF32(); - if (total_time >= CheckMessagesMaxTime) - break; + // Prevent slow packets from completely destroying the frame rate. + // This usually happens due to clumps of avatars taking huge amount + // of network processing time (which needs to be fixed, but this is + // a good limit anyway). + total_time = check_message_timer.getElapsedTimeF32(); + if (total_time >= CheckMessagesMaxTime) + break; #endif - } + } - // Handle per-frame message system processing. - gMessageSystem->processAcks(gSavedSettings.getF32("AckCollectTime")); + // Handle per-frame message system processing. + lmc.processAcks(gSavedSettings.getF32("AckCollectTime")); + } #ifdef TIME_THROTTLE_MESSAGES if (total_time >= CheckMessagesMaxTime) diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 4d25e8b2a3..ee12c451eb 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -1534,12 +1534,14 @@ bool idle_startup() { LLStartUp::setStartupState( STATE_AGENT_SEND ); } - LLMessageSystem* msg = gMessageSystem; - while (msg->checkAllMessages(gFrameCount, gServicePump)) { - display_startup(); + LockMessageChecker lmc(gMessageSystem); + while (lmc.checkAllMessages(gFrameCount, gServicePump)) + { + display_startup(); + } + lmc.processAcks(); } - msg->processAcks(); display_startup(); return FALSE; } @@ -1589,25 +1591,27 @@ bool idle_startup() //--------------------------------------------------------------------- if (STATE_AGENT_WAIT == LLStartUp::getStartupState()) { - LLMessageSystem* msg = gMessageSystem; - while (msg->checkAllMessages(gFrameCount, gServicePump)) { - if (gAgentMovementCompleted) - { - // Sometimes we have more than one message in the - // queue. break out of this loop and continue - // processing. If we don't, then this could skip one - // or more login steps. - break; - } - else + LockMessageChecker lmc(gMessageSystem); + while (lmc.checkAllMessages(gFrameCount, gServicePump)) { - LL_DEBUGS("AppInit") << "Awaiting AvatarInitComplete, got " - << msg->getMessageName() << LL_ENDL; + if (gAgentMovementCompleted) + { + // Sometimes we have more than one message in the + // queue. break out of this loop and continue + // processing. If we don't, then this could skip one + // or more login steps. + break; + } + else + { + LL_DEBUGS("AppInit") << "Awaiting AvatarInitComplete, got " + << gMessageSystem->getMessageName() << LL_ENDL; + } + display_startup(); } - display_startup(); + lmc.processAcks(); } - msg->processAcks(); display_startup(); -- cgit v1.3 From 6148a6443af886f9b71b4c88084db943950e146c Mon Sep 17 00:00:00 2001 From: Nicky Dasmijn Date: Tue, 19 May 2020 00:03:19 +0200 Subject: Remove DirectX.cmake. With recent SDKs (dating back to at least VS 2013 and the 8.1 SDK) DirectX is included in the SDK and does not need any special detection logic. --- indra/cmake/CMakeLists.txt | 1 - indra/cmake/DirectX.cmake | 36 ------------------------------------ indra/llwindow/CMakeLists.txt | 1 - indra/newview/CMakeLists.txt | 1 - 4 files changed, 39 deletions(-) delete mode 100644 indra/cmake/DirectX.cmake (limited to 'indra/newview') diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 84e1c5d6fd..7864271c02 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -22,7 +22,6 @@ set(cmake_SOURCE_FILES Copy3rdPartyLibs.cmake DBusGlib.cmake DeploySharedLibs.cmake - DirectX.cmake DragDrop.cmake EXPAT.cmake FindAPR.cmake diff --git a/indra/cmake/DirectX.cmake b/indra/cmake/DirectX.cmake deleted file mode 100644 index be9797b575..0000000000 --- a/indra/cmake/DirectX.cmake +++ /dev/null @@ -1,36 +0,0 @@ -# -*- cmake -*- - -if (WINDOWS) - find_path(DIRECTX_INCLUDE_DIR dxdiag.h) - if (DIRECTX_INCLUDE_DIR) - if (NOT DIRECTX_FIND_QUIETLY) - message(STATUS "Found DirectX include: ${DIRECTX_INCLUDE_DIR}") - endif (NOT DIRECTX_FIND_QUIETLY) - else (DIRECTX_INCLUDE_DIR) - message(FATAL_ERROR "Could not find DirectX SDK Include") - endif (DIRECTX_INCLUDE_DIR) - - # dxhint isn't meant to be the hard-coded DIRECTX_LIBRARY_DIR, we're just - # suggesting it as a hint to find_library(). The Windows SDK version number - # is embedded in the DIRECTX_INCLUDE_DIR path string after Include and Lib, - # which is why we don't just append a relative path: if there are multiple - # versions installed on the host, we need to be sure we're using THE SAME - # version. - string(REPLACE "/Include/" "/Lib/" dxhint "${DIRECTX_INCLUDE_DIR}") - if (ADDRESS_SIZE EQUAL 32) - set(archdir x86) - else() - set(archdir x64) - endif() - string(APPEND dxhint "/${archdir}") - find_library(DXGUID_LIBRARY dxguid HINTS "${dxhint}") - get_filename_component(DIRECTX_LIBRARY_DIR "${DXGUID_LIBRARY}" DIRECTORY) - if (DIRECTX_LIBRARY_DIR) - if (NOT DIRECTX_FIND_QUIETLY) - message(STATUS "Found DirectX library: ${DIRECTX_LIBRARY_DIR}") - endif (NOT DIRECTX_FIND_QUIETLY) - else (DIRECTX_LIBRARY_DIR) - message(FATAL_ERROR "Could not find DirectX SDK Libraries") - endif (DIRECTX_LIBRARY_DIR) - -endif (WINDOWS) diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 0743fd899f..5476dc324c 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -11,7 +11,6 @@ project(llwindow) include(00-Common) -include(DirectX) include(DragDrop) include(LLCommon) include(LLImage) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index a01e6ee4f6..6f21ec93cc 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -15,7 +15,6 @@ include(BuildPackagesInfo) include(BuildVersion) include(CMakeCopyIfDifferent) include(DBusGlib) -include(DirectX) include(DragDrop) include(EXPAT) include(FMODEX) -- cgit v1.3 From e29ba3bbdaf70127944389d3b4910d47dcf02aff Mon Sep 17 00:00:00 2001 From: Nicky Dasmijn Date: Tue, 19 May 2020 19:17:32 +0200 Subject: Remove more traces of find_library to search for DirectX and instead rely on the SDK setup. Remove old dinput8 import library as it is not needed --- indra/newview/CMakeLists.txt | 10 +--------- indra/win_crash_logger/CMakeLists.txt | 4 +--- 2 files changed, 2 insertions(+), 12 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 6f21ec93cc..460b2e6985 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1582,20 +1582,12 @@ if (WINDOWS) list(APPEND viewer_SOURCE_FILES ${viewer_RESOURCE_FILES}) endif (NOT USESYSTEMLIBS) - find_library(DINPUT_LIBRARY dinput8 ${DIRECTX_LIBRARY_DIR}) - find_library(DXGUID_LIBRARY dxguid ${DIRECTX_LIBRARY_DIR}) - mark_as_advanced( - DINPUT_LIBRARY - DXGUID_LIBRARY - ) - # see EXP-1765 - theory is opengl32.lib needs to be included before gdi32.lib (windows libs) set(viewer_LIBRARIES opengl32 ${WINDOWS_LIBRARIES} comdlg32 - ${DINPUT_LIBRARY} - ${DXGUID_LIBRARY} + dxguid kernel32 odbc32 odbccp32 diff --git a/indra/win_crash_logger/CMakeLists.txt b/indra/win_crash_logger/CMakeLists.txt index 1b038c26b5..86aa655f03 100644 --- a/indra/win_crash_logger/CMakeLists.txt +++ b/indra/win_crash_logger/CMakeLists.txt @@ -68,8 +68,6 @@ list(APPEND ${win_crash_logger_RESOURCE_FILES} ) -find_library(DXGUID_LIBRARY dxguid ${DIRECTX_LIBRARY_DIR}) - add_executable(windows-crash-logger WIN32 ${win_crash_logger_SOURCE_FILES}) @@ -87,7 +85,7 @@ target_link_libraries(windows-crash-logger ${BOOST_CONTEXT_LIBRARY} ${BOOST_FIBER_LIBRARY} ${WINDOWS_LIBRARIES} - ${DXGUID_LIBRARY} + dxguid ${GOOGLE_PERFTOOLS_LIBRARIES} user32 gdi32 -- cgit v1.3 From a0356e977d15dd30878f80f5a097b07dd67d3bcd Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 27 May 2020 10:39:20 -0400 Subject: DRTVWR-476: Make LLVivoxVoiceClient::logoutOfVivox() wait for logout. It can happen that we arrive at logoutOfVivox() with some other message queued on the LLEventMailDrop in question. If logoutOfVivox() assumes that other message is logout and exits, then subsequent code gets confused. Introduce a loop to wait (with the existing timeout) for the real logout message. --- indra/newview/llvoicevivox.cpp | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 5d0b3e5dd6..27cc19072f 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -338,15 +338,15 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mPlayRequestCount(0), mAvatarNameCacheConnection(), - mIsInTuningMode(false), - mIsInChannel(false), - mIsJoiningSession(false), - mIsWaitingForFonts(false), - mIsLoggingIn(false), - mIsLoggedIn(false), - mIsProcessingChannels(false), - mIsCoroutineActive(false), - mVivoxPump("vivoxClientPump") + mIsInTuningMode(false), + mIsInChannel(false), + mIsJoiningSession(false), + mIsWaitingForFonts(false), + mIsLoggingIn(false), + mIsLoggedIn(false), + mIsProcessingChannels(false), + mIsCoroutineActive(false), + mVivoxPump("vivoxClientPump") { mSpeakerVolume = scale_speaker_volume(0); @@ -1255,15 +1255,22 @@ void LLVivoxVoiceClient::logoutOfVivox(bool wait) if (wait) { LLSD timeoutResult(LLSDMap("logout", "timeout")); + LLSD result; - LL_DEBUGS("Voice") - << "waiting for logout response on " - << mVivoxPump.getName() - << LL_ENDL; - - LLSD result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); - - LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + do + { + LL_DEBUGS("Voice") + << "waiting for logout response on " + << mVivoxPump.getName() + << LL_ENDL; + + result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult); + + LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL; + // Don't get confused by prior queued events -- note that it's + // very important that mVivoxPump is an LLEventMailDrop, which + // does queue events. + } while (! result["logout"]); } else { -- cgit v1.3 From 1f4fbc8d11d37efbd6c2f2336f33b7e2eb010986 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 27 May 2020 12:36:59 -0400 Subject: DRTVWR-476, VOICE-88, SL-13025: Use a new port every SLVoice launch. The observed failure is that SLVoice, on relaunch, produces an error that bind() returned EADDRINUSE and terminates. Using a different port every time we relaunch avoids that collision. --- indra/newview/llvoicevivox.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 27cc19072f..42a1cf95a7 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -806,6 +806,21 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() LLProcess::Params params; params.executable = exe_path; + // VOICE-88: Cycle through [portbase..portbase+portrange) on + // successive tries because attempting to relaunch (after manually + // disabling and then re-enabling voice) with the same port can + // cause SLVoice's bind() call to fail with EADDRINUSE. We expect + // that eventually the OS will time out previous ports, which is + // why we cycle instead of incrementing indefinitely. + U32 portbase = gSavedSettings.getU32("VivoxVoicePort"); + static U32 portoffset = 0; + static const U32 portrange = 100; + std::string host(gSavedSettings.getString("VivoxVoiceHost")); + U32 port = portbase + portoffset; + portoffset = (portoffset + 1) % portrange; + params.args.add("-i"); + params.args.add(STRINGIZE(host << ':' << port)); + std::string loglevel = gSavedSettings.getString("VivoxDebugLevel"); if (loglevel.empty()) { @@ -862,7 +877,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() sGatewayPtr = LLProcess::create(params); - mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort")); + mDaemonHost = LLHost(host.c_str(), port); } else { -- cgit v1.3 From e2dd15397c709a3a37e70942d40373f5476a434b Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 20 May 2020 15:37:21 -0700 Subject: SL-9756: Take the "session_id" from the offline message that was passed. --- indra/newview/llimprocessing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index 6da7bbe263..b534fc0b4a 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -1608,7 +1608,7 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) message_data["to_agent_id"].asUUID(), IM_OFFLINE, (EInstantMessage)message_data["dialog"].asInteger(), - LLUUID::null, // session id, since there is none we can only use frienship/group invite caps + message_data["session_id"].asUUID(), message_data["timestamp"].asInteger(), message_data["from_agent_name"].asString(), message_data["message"].asString(), -- cgit v1.3 From c8b7466c1999cc834b90c9aed76d8548b66032c6 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Mon, 8 Jun 2020 11:28:05 -0700 Subject: SL-11430, SL-9756: Take transaction-id from offline messages. Correct LLSD names. Use offline flag rather than implicit tests of session_id and aux_id. --- indra/newview/llimprocessing.cpp | 61 +++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 26 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index b534fc0b4a..60f1de4e8c 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -857,7 +857,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, } else // IM_TASK_INVENTORY_OFFERED { - if (offline == IM_OFFLINE && session_id.isNull() && aux_id.notNull() && binary_bucket_size > sizeof(S8)* 5) + if (offline) { // cap received offline message std::string str_bucket = ll_safe_string((char*)binary_bucket, binary_bucket_size); @@ -889,9 +889,10 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, info->mIM = dialog; info->mFromID = from_id; info->mFromGroup = from_group; - info->mTransactionID = session_id; info->mFolderID = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(info->mType)); + info->mTransactionID = session_id.notNull() ? session_id : aux_id; + info->mFromName = name; info->mDesc = message; info->mHost = sender; @@ -1569,7 +1570,7 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) return; } - if (gAgent.getRegion() == NULL) + if (!gAgent.getRegion()) { LL_WARNS("Messaging") << "Region null while attempting to load messages." << LL_ENDL; return; @@ -1577,8 +1578,8 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) LL_INFOS("Messaging") << "Processing offline messages." << LL_ENDL; - std::vector data; - S32 binary_bucket_size = 0; +// std::vector data; +// S32 binary_bucket_size = 0; LLHost sender = gAgent.getRegionHost(); LLSD::array_iterator i = messages.beginArray(); @@ -1587,38 +1588,46 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) { const LLSD &message_data(*i); - LLVector3 position(message_data["local_x"].asReal(), message_data["local_y"].asReal(), message_data["local_z"].asReal()); - data = message_data["binary_bucket"].asBinary(); - binary_bucket_size = data.size(); // message_data["count"] always 0 - U32 parent_estate_id = message_data.has("parent_estate_id") ? message_data["parent_estate_id"].asInteger() : 1; // 1 - IMMainland + /* RIDER: Many fields in this message are using a '_' rather than the standard '-'. This + * should be changed but would require tight coordination with the simulator. + */ + LLVector3 position; + if (message_data.has("position")) + { + position.setValue(message_data["position"]); + } + else + { + position.set(message_data["local_x"].asReal(), message_data["local_y"].asReal(), message_data["local_z"].asReal()); + } - // Todo: once dirtsim-369 releases, remove one of the int/str options - BOOL from_group; - if (message_data["from_group"].isInteger()) + std::vector bin_bucket; + if (message_data.has("binary_bucket")) { - from_group = message_data["from_group"].asInteger(); + bin_bucket = message_data["binary_bucket"].asBinary(); } else { - from_group = message_data["from_group"].asString() == "Y"; + bin_bucket.push_back(0); } - LLIMProcessing::processNewMessage(message_data["from_agent_id"].asUUID(), - from_group, + LLIMProcessing::processNewMessage( + message_data["from_id"].asUUID(), + message_data["from_group"].asBoolean(), message_data["to_agent_id"].asUUID(), - IM_OFFLINE, - (EInstantMessage)message_data["dialog"].asInteger(), - message_data["session_id"].asUUID(), - message_data["timestamp"].asInteger(), - message_data["from_agent_name"].asString(), - message_data["message"].asString(), - parent_estate_id, + static_cast(message_data["offline"].asInteger()), + static_cast(message_data["dialog"].asInteger()), + message_data["transaction-id"].asUUID(), + static_cast(message_data["timestamp"].asInteger()), + message_data["from_name"].asString(), + (message_data.has("message")) ? message_data["message"].asString() : std::string(), + static_cast((message_data.has("parent_estate_id")) ? message_data["parent_estate_id"].asInteger() : 1), // 1 - IMMainland message_data["region_id"].asUUID(), position, - &data[0], - binary_bucket_size, + bin_bucket.data(), + bin_bucket.size(), sender, - message_data["asset_id"].asUUID()); // not necessarily an asset + message_data["asset_id"].asUUID()); } } -- cgit v1.3 From f8e53adce7c089abe9a50c353d14f7a548ce3f95 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Thu, 18 Jun 2020 12:56:00 -0700 Subject: SL-9756: Get session_id/transaction id from aux if session is missing. --- indra/newview/llimprocessing.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/newview') diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index 60f1de4e8c..c274267b21 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -1628,6 +1628,7 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) bin_bucket.size(), sender, message_data["asset_id"].asUUID()); + } } -- cgit v1.3 From f72759c16d74d6a996b4b63f610b50c80c3db825 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Mon, 22 Jun 2020 10:55:05 -0700 Subject: SL-9756: IM_TASK_INVENTORY_OFFERED bucket offline format conforms to the online format. --- indra/newview/llimprocessing.cpp | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index c274267b21..a2900c553c 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -857,33 +857,15 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, } else // IM_TASK_INVENTORY_OFFERED { - if (offline) + if (sizeof(S8) != binary_bucket_size) { - // cap received offline message - std::string str_bucket = ll_safe_string((char*)binary_bucket, binary_bucket_size); - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("|", "", boost::keep_empty_tokens); - tokenizer tokens(str_bucket, sep); - tokenizer::iterator iter = tokens.begin(); - - info->mType = (LLAssetType::EType)(atoi((*(iter++)).c_str())); - // Note There is more elements in 'tokens' ... - - info->mObjectID = LLUUID::null; - info->mFromObject = TRUE; - } - else - { - if (sizeof(S8) != binary_bucket_size) - { - LL_WARNS("Messaging") << "Malformed inventory offer from object" << LL_ENDL; - delete info; - break; - } - info->mType = (LLAssetType::EType) binary_bucket[0]; - info->mObjectID = LLUUID::null; - info->mFromObject = TRUE; + LL_WARNS("Messaging") << "Malformed inventory offer from object" << LL_ENDL; + delete info; + break; } + info->mType = (LLAssetType::EType) binary_bucket[0]; + info->mObjectID = LLUUID::null; + info->mFromObject = TRUE; } info->mIM = dialog; -- cgit v1.3 From 01f2308c85dd02d199072006e6b9ff42c90a1986 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Mon, 22 Jun 2020 16:08:11 -0700 Subject: SL-9756: Get the LLSD names right. --- indra/newview/llimprocessing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index a2900c553c..fc209c2eae 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -1594,7 +1594,7 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) } LLIMProcessing::processNewMessage( - message_data["from_id"].asUUID(), + message_data["from_agent_id"].asUUID(), message_data["from_group"].asBoolean(), message_data["to_agent_id"].asUUID(), static_cast(message_data["offline"].asInteger()), -- cgit v1.3 From 4708662091760f90a7782b726a5a7d89f376ce53 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 Jun 2020 10:29:15 -0400 Subject: SL-13361: Distill redundant create_console() code to set_stream(). There are separate stanzas in llappviewerwin32.cpp's create_console() function for each of STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and STD_ERROR_HANDLE. SL-13361 wants to add more code to each. Factor out new local set_stream() function and make create_console() call it three times. (cherry picked from commit 13b78a0c5a788c617866e3530c65dae616e6520f) --- indra/newview/llappviewerwin32.cpp | 59 +++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 30 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 9a8a5f16bb..1f66177c37 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -501,11 +501,12 @@ void LLAppViewerWin32::disableWinErrorReporting() const S32 MAX_CONSOLE_LINES = 500; -static bool create_console() -{ - int h_con_handle; - intptr_t l_std_handle; +namespace { + +FILE* set_stream(const char* which, DWORD handle_id, const char* mode); +bool create_console() +{ CONSOLE_SCREEN_BUFFER_INFO coninfo; FILE *fp; @@ -518,50 +519,48 @@ static bool create_console() SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); // redirect unbuffered STDOUT to the console - l_std_handle = reinterpret_cast(GetStdHandle(STD_OUTPUT_HANDLE)); - h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); - if (h_con_handle == -1) + FILE* fp = set_stream("stdout", STD_OUTPUT_HANDLE, "w"); + if (fp) { - LL_WARNS() << "create_console() failed to open stdout handle" << LL_ENDL; - } - else - { - fp = _fdopen( h_con_handle, "w" ); *stdout = *fp; - setvbuf( stdout, NULL, _IONBF, 0 ); } // redirect unbuffered STDIN to the console - l_std_handle = reinterpret_cast(GetStdHandle(STD_INPUT_HANDLE)); - h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); - if (h_con_handle == -1) - { - LL_WARNS() << "create_console() failed to open stdin handle" << LL_ENDL; - } - else + fp = set_stream("stdin", STD_INPUT_HANDLE, "r"); + if (fp) { - fp = _fdopen( h_con_handle, "r" ); *stdin = *fp; - setvbuf( stdin, NULL, _IONBF, 0 ); } // redirect unbuffered STDERR to the console - l_std_handle = reinterpret_cast(GetStdHandle(STD_ERROR_HANDLE)); - h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); + fp = set_stream("stderr", STD_ERROR_HANDLE, "w"); + if (fp) + { + *stderr = *fp; + } + + return isConsoleAllocated; +} + +FILE* set_stream(const char* which, DWORD handle_id, const char* mode) +{ + long l_std_handle = (long)GetStdHandle(handle_id); + int h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); if (h_con_handle == -1) { - LL_WARNS() << "create_console() failed to open stderr handle" << LL_ENDL; + LL_WARNS() << "create_console() failed to open " << which << " handle" << LL_ENDL; + return nullptr; } else { - fp = _fdopen( h_con_handle, "w" ); - *stderr = *fp; - setvbuf( stderr, NULL, _IONBF, 0 ); + FILE* fp = _fdopen( h_con_handle, mode ); + setvbuf( fp, NULL, _IONBF, 0 ); + return fp; } - - return isConsoleAllocated; } +} // anonymous namespace + LLAppViewerWin32::LLAppViewerWin32(const char* cmd_line) : mCmdLine(cmd_line), mIsConsoleAllocated(false) -- cgit v1.3 From d8649dbb8a5a20753248923a25c13f729cadd99a Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 2 Jun 2020 16:44:22 -0400 Subject: SL-13361: Enable color processing on Windows 10 debug console. (cherry picked from commit 0b61150e698537a7e42a4cdae02496da500399d9) --- indra/llcommon/llerror.cpp | 5 ++--- indra/newview/llappviewerwin32.cpp | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) (limited to 'indra/newview') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 41c4ddc725..411412c883 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -40,6 +40,8 @@ # include # include # include +#else +# include #endif // !LL_WINDOWS #include #include "string.h" @@ -236,14 +238,11 @@ namespace { static bool checkANSI(void) { -#if LL_LINUX || LL_DARWIN // Check whether it's okay to use ANSI; if stderr is // a tty then we assume yes. Can be turned off with // the LL_NO_ANSI_COLOR env var. return (0 != isatty(2)) && (NULL == getenv("LL_NO_ANSI_COLOR")); -#endif // LL_LINUX - return FALSE; // works in a cygwin shell... ;) } }; diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 1f66177c37..f0aa355342 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -500,6 +500,10 @@ void LLAppViewerWin32::disableWinErrorReporting() } const S32 MAX_CONSOLE_LINES = 500; +// Only defined in newer SDKs than we currently use +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 4 +#endif namespace { @@ -507,13 +511,11 @@ FILE* set_stream(const char* which, DWORD handle_id, const char* mode); bool create_console() { - CONSOLE_SCREEN_BUFFER_INFO coninfo; - FILE *fp; - // allocate a console for this app const bool isConsoleAllocated = AllocConsole(); // set the screen buffer to be big enough to let us scroll text + CONSOLE_SCREEN_BUFFER_INFO coninfo; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); coninfo.dwSize.Y = MAX_CONSOLE_LINES; SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); @@ -542,19 +544,24 @@ bool create_console() return isConsoleAllocated; } -FILE* set_stream(const char* which, DWORD handle_id, const char* mode) +FILE* set_stream(const char* desc, DWORD handle_id, const char* mode) { - long l_std_handle = (long)GetStdHandle(handle_id); - int h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT); + auto l_std_handle = GetStdHandle(handle_id); + int h_con_handle = _open_osfhandle(reinterpret_cast(l_std_handle), _O_TEXT); if (h_con_handle == -1) { - LL_WARNS() << "create_console() failed to open " << which << " handle" << LL_ENDL; + LL_WARNS() << "create_console() failed to open " << desc << " handle" << LL_ENDL; return nullptr; } else { FILE* fp = _fdopen( h_con_handle, mode ); setvbuf( fp, NULL, _IONBF, 0 ); + // Enable color processing on Windows 10 console windows. + DWORD dwMode = 0; + GetConsoleMode(l_std_handle, &dwMode); + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(l_std_handle, dwMode); return fp; } } -- cgit v1.3 From 01128f9f945f20bc3c472ed953cb0c0fae6ce407 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 1 Jul 2020 16:29:59 -0400 Subject: DRTVWR-476, SL-13528: Use freopen_s() instead of assigning stderr. The llappviewerwin32.cpp create_console() function called by LLAppViewerWin32::initConsole() used to assign *stderr = *(new FILE* value), and so forth for stdout and stdin. That dubious tactic no longer works with the new Windows CRT introduced with VS 2015. freopen_s() works much better. --- indra/newview/llappviewerwin32.cpp | 100 +++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 49 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index f0aa355342..156a1c5893 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -507,63 +507,65 @@ const S32 MAX_CONSOLE_LINES = 500; namespace { -FILE* set_stream(const char* which, DWORD handle_id, const char* mode); +void set_stream(const char* desc, FILE* fp, DWORD handle_id, const char* name, const char* mode="w"); bool create_console() { - // allocate a console for this app - const bool isConsoleAllocated = AllocConsole(); - - // set the screen buffer to be big enough to let us scroll text - CONSOLE_SCREEN_BUFFER_INFO coninfo; - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); - coninfo.dwSize.Y = MAX_CONSOLE_LINES; - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); - - // redirect unbuffered STDOUT to the console - FILE* fp = set_stream("stdout", STD_OUTPUT_HANDLE, "w"); - if (fp) - { - *stdout = *fp; - } - - // redirect unbuffered STDIN to the console - fp = set_stream("stdin", STD_INPUT_HANDLE, "r"); - if (fp) - { - *stdin = *fp; - } + // allocate a console for this app + const bool isConsoleAllocated = AllocConsole(); - // redirect unbuffered STDERR to the console - fp = set_stream("stderr", STD_ERROR_HANDLE, "w"); - if (fp) - { - *stderr = *fp; - } + if (isConsoleAllocated) + { + // set the screen buffer to be big enough to let us scroll text + CONSOLE_SCREEN_BUFFER_INFO coninfo; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); + coninfo.dwSize.Y = MAX_CONSOLE_LINES; + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); + + // redirect unbuffered STDOUT to the console + set_stream("stdout", stdout, STD_OUTPUT_HANDLE, "CONOUT$"); + // redirect unbuffered STDERR to the console + set_stream("stderr", stderr, STD_ERROR_HANDLE, "CONOUT$"); + // redirect unbuffered STDIN to the console + // Don't bother: our console is solely for log output. We never read stdin. +// set_stream("stdin", stdin, STD_INPUT_HANDLE, "CONIN$", "r"); + } - return isConsoleAllocated; + return isConsoleAllocated; } -FILE* set_stream(const char* desc, DWORD handle_id, const char* mode) +void set_stream(const char* desc, FILE* fp, DWORD handle_id, const char* name, const char* mode) { - auto l_std_handle = GetStdHandle(handle_id); - int h_con_handle = _open_osfhandle(reinterpret_cast(l_std_handle), _O_TEXT); - if (h_con_handle == -1) - { - LL_WARNS() << "create_console() failed to open " << desc << " handle" << LL_ENDL; - return nullptr; - } - else - { - FILE* fp = _fdopen( h_con_handle, mode ); - setvbuf( fp, NULL, _IONBF, 0 ); - // Enable color processing on Windows 10 console windows. - DWORD dwMode = 0; - GetConsoleMode(l_std_handle, &dwMode); - dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - SetConsoleMode(l_std_handle, dwMode); - return fp; - } + // SL-13528: This code used to be based on + // http://dslweb.nwnexus.com/~ast/dload/guicon.htm + // (referenced in https://stackoverflow.com/a/191880). + // But one of the comments on that StackOverflow answer points out that + // assigning to *stdout or *stderr "probably doesn't even work with the + // Universal CRT that was introduced in 2015," suggesting freopen_s() + // instead. Code below is based on https://stackoverflow.com/a/55875595. + auto std_handle = GetStdHandle(handle_id); + if (std_handle == INVALID_HANDLE_VALUE) + { + LL_WARNS() << "create_console() failed to get " << desc << " handle" << LL_ENDL; + } + else + { + if (mode == std::string("w")) + { + // Enable color processing on Windows 10 console windows. + DWORD dwMode = 0; + GetConsoleMode(std_handle, &dwMode); + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(std_handle, dwMode); + } + // Redirect the passed fp to the console. + FILE* ignore; + if (freopen_s(&ignore, name, mode, fp) == 0) + { + // use unbuffered I/O + setvbuf( fp, NULL, _IONBF, 0 ); + } + } } } // anonymous namespace -- cgit v1.3 From 766b21a0a624ffb75c06801cf8ea3573e6dc4b5d Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 1 Jul 2020 16:52:44 -0700 Subject: SL-13533: Use the old name for from_agent_name SL-13540: Do not fail if binary bucket is too large, attempt to extract the asset type from the old style bucket. Notification still not shown. --- indra/newview/llimprocessing.cpp | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index fc209c2eae..301b4c9214 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -857,13 +857,28 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, } else // IM_TASK_INVENTORY_OFFERED { - if (sizeof(S8) != binary_bucket_size) + if (sizeof(S8) == binary_bucket_size) { - LL_WARNS("Messaging") << "Malformed inventory offer from object" << LL_ENDL; - delete info; - break; + info->mType = (LLAssetType::EType) binary_bucket[0]; + } + else + { + /*RIDER*/ // The previous version of the protocol returned the wrong binary bucket... we + // still might be able to figure out the type... even though the offer is not retrievable. + std::string str_bucket(reinterpret_cast(binary_bucket)); + std::string str_type(str_bucket.substr(0, str_bucket.find('|'))); + + std::stringstream type_convert(str_type); + + S32 type; + type_convert >> type; + + // We could try AT_UNKNOWN which would be more accurate, but that causes an auto decline + info->mType = static_cast(type); + // Don't break in the case of a bad binary bucket. Go ahead and show the + // accept/decline popup even though it will not do anything. + LL_WARNS("Messaging") << "Malformed inventory offer from object, type might be " << info->mType << LL_ENDL; } - info->mType = (LLAssetType::EType) binary_bucket[0]; info->mObjectID = LLUUID::null; info->mFromObject = TRUE; } @@ -1601,8 +1616,8 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) static_cast(message_data["dialog"].asInteger()), message_data["transaction-id"].asUUID(), static_cast(message_data["timestamp"].asInteger()), - message_data["from_name"].asString(), - (message_data.has("message")) ? message_data["message"].asString() : std::string(), + message_data["from_agent_name"].asString(), + message_data["message"].asString(), static_cast((message_data.has("parent_estate_id")) ? message_data["parent_estate_id"].asInteger() : 1), // 1 - IMMainland message_data["region_id"].asUUID(), position, -- cgit v1.3 From bf5585c0ec9d6c2159a203ebdbd1b639689a5c50 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 2 Jul 2020 13:03:57 +0300 Subject: SL-13540 Offline scripted inventory offers not shown on non drtsim-451 --- indra/newview/llimprocessing.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index 301b4c9214..5c9d53e0b9 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -865,6 +865,8 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, { /*RIDER*/ // The previous version of the protocol returned the wrong binary bucket... we // still might be able to figure out the type... even though the offer is not retrievable. + + // Should be safe to remove once DRTSIM-451 fully deploys std::string str_bucket(reinterpret_cast(binary_bucket)); std::string str_type(str_bucket.substr(0, str_bucket.find('|'))); @@ -1575,8 +1577,6 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) LL_INFOS("Messaging") << "Processing offline messages." << LL_ENDL; -// std::vector data; -// S32 binary_bucket_size = 0; LLHost sender = gAgent.getRegionHost(); LLSD::array_iterator i = messages.beginArray(); @@ -1608,11 +1608,22 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) bin_bucket.push_back(0); } + // Todo: once drtsim-451 releases, remove the string option + BOOL from_group; + if (message_data["from_group"].isInteger()) + { + from_group = message_data["from_group"].asInteger(); + } + else + { + from_group = message_data["from_group"].asString() == "Y"; + } + LLIMProcessing::processNewMessage( message_data["from_agent_id"].asUUID(), - message_data["from_group"].asBoolean(), + from_group, message_data["to_agent_id"].asUUID(), - static_cast(message_data["offline"].asInteger()), + message_data.has("offline") ? static_cast(message_data["offline"].asInteger()) : IM_OFFLINE, static_cast(message_data["dialog"].asInteger()), message_data["transaction-id"].asUUID(), static_cast(message_data["timestamp"].asInteger()), -- cgit v1.3 From 72423372d6cd7f763a5567ad75752fa4e7131d60 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 20 Jul 2020 15:04:30 -0400 Subject: Increment viewer version to 6.4.6 following promotion of DRTVWR-476 --- indra/newview/VIEWER_VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview') diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 7d765dabde..3d05e8cfb4 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.4.5 +6.4.6 -- cgit v1.3